static int convert_mlt_to_av_cs( mlt_image_format format ) { int value = 0; switch( format ) { case mlt_image_rgb24: value = AV_PIX_FMT_RGB24; break; case mlt_image_rgb24a: case mlt_image_opengl: value = AV_PIX_FMT_RGBA; break; case mlt_image_yuv422: value = AV_PIX_FMT_YUYV422; break; case mlt_image_yuv420p: value = AV_PIX_FMT_YUV420P; break; default: mlt_log_error( NULL, "[filter avcolor_space] Invalid format %s\n", mlt_image_format_name( format ) ); break; } return value; }
int mlt_consumer_init( mlt_consumer self, void *child, mlt_profile profile ) { int error = 0; memset( self, 0, sizeof( struct mlt_consumer_s ) ); self->child = child; error = mlt_service_init( &self->parent, self ); if ( error == 0 ) { // Get the properties from the service mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent ); // Apply profile to properties if ( profile == NULL ) { // Normally the application creates the profile and controls its lifetime // This is the fallback exception handling profile = mlt_profile_init( NULL ); mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_properties_set_data( properties, "_profile", profile, 0, (mlt_destructor)mlt_profile_close, NULL ); } apply_profile_properties( self, profile, properties ); // Default rescaler for all consumers mlt_properties_set( properties, "rescale", "bilinear" ); // Default read ahead buffer size mlt_properties_set_int( properties, "buffer", 25 ); mlt_properties_set_int( properties, "drop_max", 5 ); // Default audio frequency and channels mlt_properties_set_int( properties, "frequency", 48000 ); mlt_properties_set_int( properties, "channels", 2 ); // Default of all consumers is real time mlt_properties_set_int( properties, "real_time", 1 ); // Default to environment test card mlt_properties_set( properties, "test_card", mlt_environment( "MLT_TEST_CARD" ) ); // Hmm - default all consumers to yuv422 :-/ self->format = mlt_image_yuv422; mlt_properties_set( properties, "mlt_image_format", mlt_image_format_name( self->format ) ); mlt_properties_set( properties, "mlt_audio_format", mlt_audio_format_name( mlt_audio_s16 ) ); mlt_events_register( properties, "consumer-frame-show", ( mlt_transmitter )mlt_consumer_frame_show ); mlt_events_register( properties, "consumer-frame-render", ( mlt_transmitter )mlt_consumer_frame_render ); mlt_events_register( properties, "consumer-stopped", NULL ); mlt_events_listen( properties, self, "consumer-frame-show", ( mlt_listener )on_consumer_frame_show ); // Register a property-changed listener to handle the profile property - // subsequent properties can override the profile self->event_listener = mlt_events_listen( properties, self, "property-changed", ( mlt_listener )mlt_consumer_property_changed ); // Create the push mutex and condition pthread_mutex_init( &self->put_mutex, NULL ); pthread_cond_init( &self->put_cond, NULL ); } return error; }
static void apply_lut( mlt_filter filter, uint8_t* image, mlt_image_format format, int width, int height ) { private_data* self = (private_data*)filter->child; uint8_t* rlut = malloc( sizeof(self->rlut) ); uint8_t* glut = malloc( sizeof(self->glut) ); uint8_t* blut = malloc( sizeof(self->blut) ); int total = width * height + 1; uint8_t* sample = image; // Copy the LUT so that we can be frame-thread safe. mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); memcpy( rlut, self->rlut, sizeof(self->rlut) ); memcpy( glut, self->glut, sizeof(self->glut) ); memcpy( blut, self->blut, sizeof(self->blut) ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); switch( format ) { case mlt_image_rgb24: while( --total ) { *sample = rlut[ *sample ]; sample++; *sample = glut[ *sample ]; sample++; *sample = blut[ *sample ]; sample++; } break; case mlt_image_rgb24a: while( --total ) { *sample = rlut[ *sample ]; sample++; *sample = glut[ *sample ]; sample++; *sample = blut[ *sample ]; sample++; sample++; // Skip alpha } break; default: mlt_log_error( MLT_FILTER_SERVICE( filter ), "Invalid image format: %s\n", mlt_image_format_name( format ) ); break; } free( rlut ); free( glut ); free( blut ); }
static int setup_sdl_video( consumer_sdl self ) { int error = 0; int sdl_flags = SDL_WINDOW_RESIZABLE; int texture_format = SDL_PIXELFORMAT_YUY2; // Skip this if video is disabled. int video_off = mlt_properties_get_int( self->properties, "video_off" ); int preview_off = mlt_properties_get_int( self->properties, "preview_off" ); if ( video_off || preview_off ) return error; if (!SDL_WasInit(SDL_INIT_VIDEO)) { pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_VIDEO ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to initialize SDL: %s\n", SDL_GetError() ); return -1; } } #if 0 // only yuv422 working currently int image_format = mlt_properties_get_int( self->properties, "mlt_image_format" ); if ( image_format ) switch ( image_format ) { case mlt_image_rgb24: texture_format = SDL_PIXELFORMAT_RGB24; break; case mlt_image_rgb24a: texture_format = SDL_PIXELFORMAT_ABGR8888; break; case mlt_image_yuv420p: texture_format = SDL_PIXELFORMAT_IYUV; break; case mlt_image_yuv422: texture_format = SDL_PIXELFORMAT_YUY2; break; default: mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Invalid image format %s\n", mlt_image_format_name( image_format ) ); return -1; } #endif if ( mlt_properties_get_int( self->properties, "fullscreen" ) ) { self->window_width = self->width; self->window_height = self->height; sdl_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; SDL_ShowCursor( SDL_DISABLE ); } pthread_mutex_lock( &mlt_sdl_mutex ); self->sdl_window = SDL_CreateWindow("MLT", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, self->window_width, self->window_height, sdl_flags); self->sdl_renderer = SDL_CreateRenderer(self->sdl_window, -1, SDL_RENDERER_ACCELERATED); if ( self->sdl_renderer ) { self->sdl_texture = SDL_CreateTexture( self->sdl_renderer, texture_format, SDL_TEXTUREACCESS_STREAMING, self->window_width, self->window_height ); if ( self->sdl_texture ) { SDL_SetRenderDrawColor( self->sdl_renderer, 0, 0, 0, 255); } else { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL texture: %s\n", SDL_GetError() ); error = -1; } } else { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL renderer: %s\n", SDL_GetError() ); error = -1; } pthread_mutex_unlock( &mlt_sdl_mutex ); return error; }
static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { // Obtain properties of frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Obtain the producer for this frame mlt_producer producer = mlt_properties_get_data( properties, "producer_colour", NULL ); mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); // Obtain properties of producer mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); // Get the current and previous colour strings char *now = mlt_properties_get( producer_props, "resource" ); char *then = mlt_properties_get( producer_props, "_resource" ); // Get the current image and dimensions cached in the producer int size = 0; uint8_t *image = mlt_properties_get_data( producer_props, "image", &size ); int current_width = mlt_properties_get_int( producer_props, "_width" ); int current_height = mlt_properties_get_int( producer_props, "_height" ); mlt_image_format current_format = mlt_properties_get_int( producer_props, "_format" ); // Parse the colour if ( now && strchr( now, '/' ) ) { now = strdup( strrchr( now, '/' ) + 1 ); mlt_properties_set( producer_props, "resource", now ); free( now ); now = mlt_properties_get( producer_props, "resource" ); } mlt_color color = mlt_properties_get_color( producer_props, "resource" ); if ( mlt_properties_get( producer_props, "mlt_image_format") ) *format = mlt_image_format_id( mlt_properties_get( producer_props, "mlt_image_format") ); // Choose suitable out values if nothing specific requested if ( *format == mlt_image_none || *format == mlt_image_glsl ) *format = mlt_image_rgb24a; if ( *width <= 0 ) *width = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->width; if ( *height <= 0 ) *height = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->height; // Choose default image format if specific request is unsupported if (*format!=mlt_image_yuv420p && *format!=mlt_image_yuv422 && *format!=mlt_image_rgb24 && *format!= mlt_image_glsl && *format!= mlt_image_glsl_texture) *format = mlt_image_rgb24a; // See if we need to regenerate if ( !now || ( then && strcmp( now, then ) ) || *width != current_width || *height != current_height || *format != current_format ) { // Color the image int i = *width * *height + 1; int bpp; // Allocate the image size = mlt_image_format_size( *format, *width, *height, &bpp ); uint8_t *p = image = mlt_pool_alloc( size ); // Update the producer mlt_properties_set_data( producer_props, "image", image, size, mlt_pool_release, NULL ); mlt_properties_set_int( producer_props, "_width", *width ); mlt_properties_set_int( producer_props, "_height", *height ); mlt_properties_set_int( producer_props, "_format", *format ); mlt_properties_set( producer_props, "_resource", now ); mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); switch ( *format ) { case mlt_image_yuv420p: { int plane_size = *width * *height; uint8_t y, u, v; RGB2YUV_601_SCALED( color.r, color.g, color.b, y, u, v ); memset(p + 0, y, plane_size); memset(p + plane_size, u, plane_size/4); memset(p + plane_size + plane_size/4, v, plane_size/4); mlt_properties_set_int( properties, "colorspace", 601 ); break; } case mlt_image_yuv422: { int uneven = *width % 2; int count = ( *width - uneven ) / 2 + 1; uint8_t y, u, v; RGB2YUV_601_SCALED( color.r, color.g, color.b, y, u, v ); i = *height + 1; while ( --i ) { int j = count; while ( --j ) { *p ++ = y; *p ++ = u; *p ++ = y; *p ++ = v; } if ( uneven ) { *p ++ = y; *p ++ = u; } } mlt_properties_set_int( properties, "colorspace", 601 ); break; } case mlt_image_rgb24: while ( --i ) { *p ++ = color.r; *p ++ = color.g; *p ++ = color.b; } break; case mlt_image_glsl: case mlt_image_glsl_texture: memset(p, 0, size); break; case mlt_image_rgb24a: while ( --i ) { *p ++ = color.r; *p ++ = color.g; *p ++ = color.b; *p ++ = color.a; } break; default: mlt_log_error( MLT_PRODUCER_SERVICE( producer ), "invalid image format %s\n", mlt_image_format_name( *format ) ); } } else { mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); } // Create the alpha channel int alpha_size = 0; uint8_t *alpha = NULL; // Initialise the alpha if (color.a < 255 || *format == mlt_image_rgb24a) { alpha_size = *width * *height; alpha = mlt_pool_alloc( alpha_size ); if ( alpha ) memset( alpha, color.a, alpha_size ); else alpha_size = 0; } // Clone our image if (buffer && image && size > 0) { *buffer = mlt_pool_alloc( size ); memcpy( *buffer, image, size ); } // Now update properties so we free the copy after mlt_frame_set_image( frame, *buffer, size, mlt_pool_release ); mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release ); mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) ); mlt_properties_set_int( properties, "meta.media.width", *width ); mlt_properties_set_int( properties, "meta.media.height", *height ); return 0; }
static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); producer_qimage self = mlt_properties_get_data( properties, "producer_qimage", NULL ); mlt_producer producer = &self->parent; *width = mlt_properties_get_int( properties, "rescale_width" ); *height = mlt_properties_get_int( properties, "rescale_height" ); mlt_service_lock( MLT_PRODUCER_SERVICE( &self->parent ) ); // Refresh the image self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); self->qimage = mlt_cache_item_data( self->qimage_cache, NULL ); self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" ); self->current_image = mlt_cache_item_data( self->image_cache, NULL ); self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" ); self->current_alpha = mlt_cache_item_data( self->alpha_cache, NULL ); refresh_image( self, frame, *format, *width, *height ); // Get width and height (may have changed during the refresh) *width = mlt_properties_get_int( properties, "width" ); *height = mlt_properties_get_int( properties, "height" ); *format = self->format; // NB: Cloning is necessary with this producer (due to processing of images ahead of use) // The fault is not in the design of mlt, but in the implementation of the qimage producer... if ( self->current_image ) { // Clone the image and the alpha int image_size = mlt_image_format_size( self->format, self->current_width, self->current_height, NULL ); uint8_t *image_copy = mlt_pool_alloc( image_size ); memcpy( image_copy, self->current_image, image_size ); // Now update properties so we free the copy after mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); // We're going to pass the copy on *buffer = image_copy; mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "%dx%d (%s)\n", self->current_width, self->current_height, mlt_image_format_name( *format ) ); // Clone the alpha channel if ( self->current_alpha ) { image_copy = mlt_pool_alloc( self->current_width * self->current_height ); memcpy( image_copy, self->current_alpha, self->current_width * self->current_height ); mlt_frame_set_alpha( frame, image_copy, self->current_width * self->current_height, mlt_pool_release ); } } else { error = 1; } // Release references and locks mlt_cache_item_close( self->qimage_cache ); mlt_cache_item_close( self->image_cache ); mlt_cache_item_close( self->alpha_cache ); mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) ); return error; }
static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { /* Obtain properties of frame */ mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); /* Obtain the producer for this frame */ producer_ktitle this = mlt_properties_get_data( properties, "producer_kdenlivetitle", NULL ); /* Obtain properties of producer */ mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( &this->parent ); *width = mlt_properties_get_int( properties, "rescale_width" ); *height = mlt_properties_get_int( properties, "rescale_height" ); mlt_service_lock( MLT_PRODUCER_SERVICE( &this->parent ) ); /* Allocate the image */ *format = mlt_image_rgb24a; mlt_position time = mlt_producer_position( &this->parent ) + mlt_producer_get_in( &this->parent ); if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { if (mlt_properties_get_int( producer_props, "force_reload" ) > 1) read_xml(producer_props); mlt_properties_set_int( producer_props, "force_reload", 0 ); drawKdenliveTitle( this, frame, *width, *height, time, 1); } else drawKdenliveTitle( this, frame, *width, *height, time, 0); // Get width and height (may have changed during the refresh) *width = mlt_properties_get_int( properties, "width" ); *height = mlt_properties_get_int( properties, "height" ); if ( this->current_image ) { // Clone the image and the alpha int image_size = this->current_width * ( this->current_height ) * 4; uint8_t *image_copy = mlt_pool_alloc( image_size ); memcpy( image_copy, this->current_image, image_size ); // Now update properties so we free the copy after mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); // We're going to pass the copy on *buffer = image_copy; mlt_log_debug( MLT_PRODUCER_SERVICE( &this->parent ), "width:%d height:%d %s\n", *width, *height, mlt_image_format_name( *format ) ); } mlt_service_unlock( MLT_PRODUCER_SERVICE( &this->parent ) ); return 0; }
static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_profile profile = mlt_frame_pop_service( frame ); // Get the properties from the frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Correct Width/height if necessary if ( *width == 0 || *height == 0 ) { *width = profile->width; *height = profile->height; } int left = mlt_properties_get_int( properties, "crop.left" ); int right = mlt_properties_get_int( properties, "crop.right" ); int top = mlt_properties_get_int( properties, "crop.top" ); int bottom = mlt_properties_get_int( properties, "crop.bottom" ); // Request the image at its original resolution if ( left || right || top || bottom ) { mlt_properties_set_int( properties, "rescale_width", mlt_properties_get_int( properties, "crop.original_width" ) ); mlt_properties_set_int( properties, "rescale_height", mlt_properties_get_int( properties, "crop.original_height" ) ); } // Now get the image error = mlt_frame_get_image( frame, image, format, width, height, writable ); int owidth = *width - left - right; int oheight = *height - top - bottom; owidth = owidth < 0 ? 0 : owidth; oheight = oheight < 0 ? 0 : oheight; if ( ( owidth != *width || oheight != *height ) && error == 0 && *image != NULL && owidth > 0 && oheight > 0 ) { int bpp; // Subsampled YUV is messy and less precise. if ( *format == mlt_image_yuv422 && frame->convert_image ) { mlt_image_format requested_format = mlt_image_rgb24; frame->convert_image( frame, image, format, requested_format ); } mlt_log_debug( NULL, "[filter crop] %s %dx%d -> %dx%d\n", mlt_image_format_name(*format), *width, *height, owidth, oheight); // Provides a manual override for misreported field order if ( mlt_properties_get( properties, "meta.top_field_first" ) ) { mlt_properties_set_int( properties, "top_field_first", mlt_properties_get_int( properties, "meta.top_field_first" ) ); mlt_properties_set_int( properties, "meta.top_field_first", 0 ); } if ( top % 2 ) mlt_properties_set_int( properties, "top_field_first", !mlt_properties_get_int( properties, "top_field_first" ) ); // Create the output image int size = mlt_image_format_size( *format, owidth, oheight, &bpp ); uint8_t *output = mlt_pool_alloc( size ); if ( output ) { // Call the generic resize crop( *image, output, bpp, *width, *height, left, right, top, bottom ); // Now update the frame mlt_frame_set_image( frame, output, size, mlt_pool_release ); *image = output; } // We should resize the alpha too uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); int alpha_size = 0; mlt_properties_get_data( properties, "alpha", &alpha_size ); if ( alpha && alpha_size >= ( *width * *height ) ) { uint8_t *newalpha = mlt_pool_alloc( owidth * oheight ); if ( newalpha ) { crop( alpha, newalpha, 1, *width, *height, left, right, top, bottom ); mlt_frame_set_alpha( frame, newalpha, owidth * oheight, mlt_pool_release ); } } *width = owidth; *height = oheight; } return error; }
static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { // Nothing to do! if ( *format == output_format ) return 0; mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_log_debug( NULL, "filter_movit_convert: %s -> %s\n", mlt_image_format_name( *format ), mlt_image_format_name( output_format ) ); // Use CPU if glsl not initialized or not supported. GlslManager* glsl = GlslManager::get_instance(); if ( !glsl || !glsl->get_int("glsl_supported" ) ) return convert_on_cpu( frame, image, format, output_format ); // Do non-GL image conversions on a CPU-based image converter. if ( *format != mlt_image_glsl && output_format != mlt_image_glsl && output_format != mlt_image_glsl_texture ) return convert_on_cpu( frame, image, format, output_format ); int error = 0; int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); int img_size = mlt_image_format_size( *format, width, height, NULL ); mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); mlt_service service = MLT_PRODUCER_SERVICE(producer); GlslManager::get_instance()->lock_service( frame ); EffectChain* chain = GlslManager::get_chain( service ); MltInput* input = GlslManager::get_input( service ); if ( !chain || !input ) { GlslManager::get_instance()->unlock_service( frame ); return 2; } if ( *format != mlt_image_glsl ) { bool finalize_chain = false; if ( output_format == mlt_image_glsl_texture ) { // We might already have a texture from a previous conversion from mlt_image_glsl. glsl_texture texture = (glsl_texture) mlt_properties_get_data( properties, "movit.convert.texture", NULL ); // XXX: requires a special property set on the frame by the app for now // because we do not have reliable way to clear the texture property // when a downstream filter has changed image. if ( texture && mlt_properties_get_int( properties, "movit.convert.use_texture") ) { *image = (uint8_t*) &texture->texture; mlt_frame_set_image( frame, *image, 0, NULL ); mlt_properties_set_int( properties, "format", output_format ); *format = output_format; GlslManager::get_instance()->unlock_service( frame ); return error; } else { // Use a separate chain to convert image in RAM to OpenGL texture. // Use cached chain if available and compatible. Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); chain = (EffectChain*) producer.get_data( "movit.convert.chain" ); input = (MltInput*) producer.get_data( "movit.convert.input" ); int w = producer.get_int( "movit.convert.width" ); int h = producer.get_int( "movit.convert.height" ); mlt_image_format f = (mlt_image_format) producer.get_int( "movit.convert.format" ); if ( !chain || width != w || height != h || output_format != f ) { chain = new EffectChain( width, height ); input = new MltInput( width, height ); chain->add_input( input ); chain->add_effect( new Mlt::VerticalFlip() ); finalize_chain = true; producer.set( "movit.convert.chain", chain, 0, (mlt_destructor) delete_chain ); producer.set( "movit.convert.input", input, 0 ); producer.set( "movit.convert.width", width ); producer.set( "movit.convert.height", height ); producer.set( "movit.convert.format", output_format ); } } } if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { input->useFlatInput( chain, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, width, height ); input->set_pixel_data( *image ); } else if ( *format == mlt_image_rgb24 ) { input->useFlatInput( chain, FORMAT_RGB, width, height ); input->set_pixel_data( *image ); } else if ( *format == mlt_image_yuv420p ) { ImageFormat image_format; YCbCrFormat ycbcr_format; if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { image_format.color_space = COLORSPACE_REC_709; image_format.gamma_curve = GAMMA_REC_709; ycbcr_format.luma_coefficients = YCBCR_REC_709; } else if ( 576 == mlt_properties_get_int( properties, "height" ) ) { image_format.color_space = COLORSPACE_REC_601_625; image_format.gamma_curve = GAMMA_REC_601; ycbcr_format.luma_coefficients = YCBCR_REC_601; } else { image_format.color_space = COLORSPACE_REC_601_525; image_format.gamma_curve = GAMMA_REC_601; ycbcr_format.luma_coefficients = YCBCR_REC_601; } ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); ycbcr_format.chroma_subsampling_x = ycbcr_format.chroma_subsampling_y = 2; // TODO: make new frame properties set by producers ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); input->set_pixel_data( *image ); } else if ( *format == mlt_image_yuv422 ) { ImageFormat image_format; YCbCrFormat ycbcr_format; if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { image_format.color_space = COLORSPACE_REC_709; image_format.gamma_curve = GAMMA_REC_709; ycbcr_format.luma_coefficients = YCBCR_REC_709; } else if ( 576 == height ) { image_format.color_space = COLORSPACE_REC_601_625; image_format.gamma_curve = GAMMA_REC_601; ycbcr_format.luma_coefficients = YCBCR_REC_601; } else { image_format.color_space = COLORSPACE_REC_601_525; image_format.gamma_curve = GAMMA_REC_601; ycbcr_format.luma_coefficients = YCBCR_REC_601; } ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); ycbcr_format.chroma_subsampling_x = 2; ycbcr_format.chroma_subsampling_y = 1; // TODO: make new frame properties set by producers ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); // convert chunky to planar uint8_t* planar = (uint8_t*) mlt_pool_alloc( img_size ); yuv422_to_yuv422p( *image, planar, width, height ); input->set_pixel_data( planar ); mlt_frame_set_image( frame, planar, img_size, mlt_pool_release ); } // Finalize the separate conversion chain if needed. if ( finalize_chain ) chain->finalize(); } if ( output_format != mlt_image_glsl ) { glsl_fbo fbo = glsl->get_fbo( width, height ); if ( output_format == mlt_image_glsl_texture ) { glsl_texture texture = glsl->get_texture( width, height, GL_RGBA ); glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); check_error(); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); GlslManager::render( service, chain, fbo->fbo, width, height ); glFinish(); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); *image = (uint8_t*) &texture->texture; mlt_frame_set_image( frame, *image, 0, NULL ); mlt_properties_set_data( properties, "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL ); mlt_properties_set_int( properties, "format", output_format ); *format = output_format; } else { // Use a PBO to hold the data we read back with glReadPixels() // (Intel/DRI goes into a slow path if we don't read to PBO) GLenum gl_format = ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl )? GL_RGBA : GL_RGB; img_size = width * height * ( gl_format == GL_RGB? 3 : 4 ); glsl_pbo pbo = glsl->get_pbo( img_size ); glsl_texture texture = glsl->get_texture( width, height, gl_format ); if ( fbo && pbo && texture ) { // Set the FBO glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); check_error(); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); GlslManager::render( service, chain, fbo->fbo, width, height ); // Read FBO into PBO glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); check_error(); glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); check_error(); glReadPixels( 0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); check_error(); // Copy from PBO uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); check_error(); *image = (uint8_t*) mlt_pool_alloc( img_size ); mlt_frame_set_image( frame, *image, img_size, mlt_pool_release ); memcpy( *image, buf, img_size ); if ( output_format == mlt_image_yuv422 || output_format == mlt_image_yuv420p ) { *format = mlt_image_rgb24; error = convert_on_cpu( frame, image, format, output_format ); } // Release PBO and FBO glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); check_error(); glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); glBindTexture( GL_TEXTURE_2D, 0 ); check_error(); mlt_properties_set_data( properties, "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL); mlt_properties_set_int( properties, "format", output_format ); *format = output_format; } else { error = 1; } } if ( fbo ) GlslManager::release_fbo( fbo ); } else { mlt_properties_set_int( properties, "format", output_format ); *format = output_format; } GlslManager::get_instance()->unlock_service( frame ); return error; }
static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); int error = 0; if ( *format != output_format ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( mlt_frame_get_original_producer( frame ) ) ); int profile_colorspace = profile ? profile->colorspace : 601; int colorspace = mlt_properties_get_int( properties, "colorspace" ); int force_full_luma = 0; mlt_log_debug( NULL, "[filter avcolor_space] %s -> %s @ %dx%d space %d->%d\n", mlt_image_format_name( *format ), mlt_image_format_name( output_format ), width, height, colorspace, profile_colorspace ); int in_fmt = convert_mlt_to_av_cs( *format ); int out_fmt = convert_mlt_to_av_cs( output_format ); int size = FFMAX( avpicture_get_size( out_fmt, width, height ), mlt_image_format_size( output_format, width, height, NULL ) ); uint8_t *output = mlt_pool_alloc( size ); if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { register int len = width * height; uint8_t *alpha = mlt_pool_alloc( len ); if ( alpha ) { // Extract the alpha mask from the RGBA image using Duff's Device register uint8_t *s = *image + 3; // start on the alpha component register uint8_t *d = alpha; register int n = ( len + 7 ) / 8; switch ( len % 8 ) { case 0: do { *d++ = *s; s += 4; case 7: *d++ = *s; s += 4; case 6: *d++ = *s; s += 4; case 5: *d++ = *s; s += 4; case 4: *d++ = *s; s += 4; case 3: *d++ = *s; s += 4; case 2: *d++ = *s; s += 4; case 1: *d++ = *s; s += 4; } while ( --n > 0 ); } mlt_frame_set_alpha( frame, alpha, len, mlt_pool_release ); } } // Update the output if ( !av_convert_image( output, *image, out_fmt, in_fmt, width, height, colorspace, profile_colorspace, force_full_luma ) ) { // The new colorspace is only valid if destination is YUV. if ( output_format == mlt_image_yuv422 || output_format == mlt_image_yuv420p ) mlt_properties_set_int( properties, "colorspace", profile_colorspace ); } *image = output; *format = output_format; mlt_frame_set_image( frame, output, size, mlt_pool_release ); mlt_properties_set_int( properties, "format", output_format ); if ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl ) { register int len = width * height; int alpha_size = 0; uint8_t *alpha = mlt_frame_get_alpha( frame ); mlt_properties_get_data( properties, "alpha", &alpha_size ); if ( alpha && alpha_size >= len ) { // Merge the alpha mask from into the RGBA image using Duff's Device register uint8_t *s = alpha; register uint8_t *d = *image + 3; // start on the alpha component register int n = ( len + 7 ) / 8; switch ( len % 8 ) { case 0: do { *d = *s++; d += 4; case 7: *d = *s++; d += 4; case 6: *d = *s++; d += 4; case 5: *d = *s++; d += 4; case 4: *d = *s++; d += 4; case 3: *d = *s++; d += 4; case 2: *d = *s++; d += 4; case 1: *d = *s++; d += 4; } while ( --n > 0 ); } } } } return error; }
static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); int error = 0; if ( *format != output_format ) { int colorspace = mlt_properties_get_int( properties, "colorspace" ); int force_full_luma = -1; mlt_log_debug( NULL, "[filter avcolor_space] %s -> %s @ %dx%d space %d\n", mlt_image_format_name( *format ), mlt_image_format_name( output_format ), width, height, colorspace ); int in_fmt = convert_mlt_to_av_cs( *format ); int out_fmt = convert_mlt_to_av_cs( output_format ); int size = FFMAX( avpicture_get_size( out_fmt, width, height ), mlt_image_format_size( output_format, width, height, NULL ) ); uint8_t *output = mlt_pool_alloc( size ); if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { register int len = width * height; uint8_t *alpha = mlt_pool_alloc( len ); if ( alpha ) { // Extract the alpha mask from the RGBA image using Duff's Device register uint8_t *s = *image + 3; // start on the alpha component register uint8_t *d = alpha; register int n = ( len + 7 ) / 8; switch ( len % 8 ) { case 0: do { *d++ = *s; s += 4; case 7: *d++ = *s; s += 4; case 6: *d++ = *s; s += 4; case 5: *d++ = *s; s += 4; case 4: *d++ = *s; s += 4; case 3: *d++ = *s; s += 4; case 2: *d++ = *s; s += 4; case 1: *d++ = *s; s += 4; } while ( --n > 0 ); } mlt_frame_set_alpha( frame, alpha, len, mlt_pool_release ); } } // Update the output if ( *format == mlt_image_yuv422 && mlt_properties_get( properties, "force_full_luma" ) && ( output_format == mlt_image_rgb24 || output_format == mlt_image_rgb24a ) ) { // By removing the frame property we only permit the luma to skip scaling once. // Thereafter, we let swscale scale the luma range as it pleases since it seems // we do not have control over the RGB to YUV conversion. force_full_luma = mlt_properties_get_int( properties, "force_full_luma" ); mlt_properties_set( properties, "force_full_luma", NULL ); } av_convert_image( output, *image, out_fmt, in_fmt, width, height, colorspace, force_full_luma ); *image = output; *format = output_format; mlt_frame_set_image( frame, output, size, mlt_pool_release ); mlt_properties_set_int( properties, "format", output_format ); if ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl ) { register int len = width * height; int alpha_size = 0; uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); mlt_properties_get_data( properties, "alpha", &alpha_size ); if ( alpha && alpha_size >= len ) { // Merge the alpha mask from into the RGBA image using Duff's Device register uint8_t *s = alpha; register uint8_t *d = *image + 3; // start on the alpha component register int n = ( len + 7 ) / 8; switch ( len % 8 ) { case 0: do { *d = *s++; d += 4; case 7: *d = *s++; d += 4; case 6: *d = *s++; d += 4; case 5: *d = *s++; d += 4; case 4: *d = *s++; d += 4; case 3: *d = *s++; d += 4; case 2: *d = *s++; d += 4; case 1: *d = *s++; d += 4; } while ( --n > 0 ); } } } } return error; }