X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fxine%2Ffilter_deinterlace.c;h=70457e96dcc084eb2863f30d52f5f1d2972bc6f1;hb=7601aa4468f96fb8e3395266b194e7e7bd095453;hp=eac2a84b59f812a621323efd42d2b9730dffc651;hpb=6eeb161c04131399f55788c764d3c59b9a96cdc3;p=mlt diff --git a/src/modules/xine/filter_deinterlace.c b/src/modules/xine/filter_deinterlace.c index eac2a84b..70457e96 100644 --- a/src/modules/xine/filter_deinterlace.c +++ b/src/modules/xine/filter_deinterlace.c @@ -30,7 +30,76 @@ #include #include -int deinterlace_yadif( mlt_frame frame, mlt_filter filter, uint8_t **image, mlt_image_format *format, int *width, int *height, int mode ) +#define YADIF_MODE_TEMPORAL_SPATIAL (0) +#define YADIF_MODE_TEMPORAL (2) + +static yadif_filter *init_yadif( int width, int height ) +{ + yadif_filter *yadif = mlt_pool_alloc( sizeof( *yadif ) ); + + yadif->cpu = 0; // Pure C +#ifdef USE_SSE + yadif->cpu |= AVS_CPU_INTEGER_SSE; +#endif +#ifdef USE_SSE2 + yadif->cpu |= AVS_CPU_SSE2; +#endif + // Create intermediate planar planes + yadif->yheight = height; + yadif->ywidth = width; + yadif->uvwidth = yadif->ywidth / 2; + yadif->ypitch = ( yadif->ywidth + 15 ) / 16 * 16; + yadif->uvpitch = ( yadif->uvwidth + 15 ) / 16 * 16; + yadif->ysrc = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); + yadif->usrc = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch); + yadif->vsrc = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); + yadif->yprev = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); + yadif->uprev = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); + yadif->vprev = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); + yadif->ynext = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); + yadif->unext = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); + yadif->vnext = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); + yadif->ydest = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); + yadif->udest = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); + yadif->vdest = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); + + return yadif; +} + +static void close_yadif(yadif_filter *yadif) +{ + mlt_pool_release( yadif->ysrc ); + mlt_pool_release( yadif->usrc ); + mlt_pool_release( yadif->vsrc ); + mlt_pool_release( yadif->yprev ); + mlt_pool_release( yadif->uprev ); + mlt_pool_release( yadif->vprev ); + mlt_pool_release( yadif->ynext ); + mlt_pool_release( yadif->unext ); + mlt_pool_release( yadif->vnext ); + mlt_pool_release( yadif->ydest ); + mlt_pool_release( yadif->udest ); + mlt_pool_release( yadif->vdest ); + mlt_pool_release( yadif ); + +#if defined(__GNUC__) && !defined(PIC) + // Set SSSE3 bit to cpu + asm (\ + "mov $1, %%eax \n\t"\ + "push %%ebx \n\t"\ + "cpuid \n\t"\ + "pop %%ebx \n\t"\ + "mov %%ecx, %%edx \n\t"\ + "shr $9, %%edx \n\t"\ + "and $1, %%edx \n\t"\ + "shl $9, %%edx \n\t"\ + "and $511, %%ebx \n\t"\ + "or %%edx, %%ebx \n\t"\ + : "=b"(yadif->cpu) : "p"(yadif->cpu) : "%eax", "%ecx", "%edx"); +#endif +} + +static int deinterlace_yadif( mlt_frame frame, mlt_filter filter, uint8_t **image, mlt_image_format *format, int *width, int *height, int mode ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_frame previous_frame = mlt_properties_get_data( properties, "previous frame", NULL ); @@ -41,24 +110,27 @@ int deinterlace_yadif( mlt_frame frame, mlt_filter filter, uint8_t **image, mlt_ uint8_t* next_image = NULL; int next_width = *width; int next_height = *height; - yadif_filter *yadif = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), "yadif", NULL ); - mlt_log_debug( MLT_FILTER_SERVICE(filter), "previous %d current %d next %d\n", - previous_frame? mlt_frame_get_position(previous_frame) : -1, - mlt_frame_get_position(frame), - next_frame? mlt_frame_get_position(next_frame) : -1); + mlt_log_debug( MLT_FILTER_SERVICE(filter), "previous " MLT_POSITION_FMT " current " MLT_POSITION_FMT " next " MLT_POSITION_FMT "\n", + previous_frame? mlt_frame_original_position(previous_frame) : -1, + mlt_frame_original_position(frame), + next_frame? mlt_frame_original_position(next_frame) : -1); if ( !previous_frame || !next_frame ) return 1; // Get the preceding frame's image int error = mlt_frame_get_image( previous_frame, &previous_image, format, &previous_width, &previous_height, 0 ); + int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( previous_frame ), "progressive" ); // Check that we aren't already progressive - if ( !error && previous_image && *format == mlt_image_yuv422 && - !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" ) ) + if ( !error && previous_image && !progressive ) { + // OK, now we know we have work to do and can request the image in our format + frame->convert_image( previous_frame, &previous_image, format, mlt_image_yuv422 ); + // Get the current frame's image + *format = mlt_image_yuv422; error = mlt_frame_get_image( frame, image, format, width, height, 0 ); if ( !error && *image && *format == mlt_image_yuv422 ) @@ -68,73 +140,62 @@ int deinterlace_yadif( mlt_frame frame, mlt_filter filter, uint8_t **image, mlt_ if ( !error && next_image && *format == mlt_image_yuv422 ) { - if ( !yadif->ysrc ) + yadif_filter *yadif = init_yadif( *width, *height ); + if ( yadif ) { - // Create intermediate planar planes - yadif->yheight = *height; - yadif->ywidth = *width; - yadif->uvwidth = yadif->ywidth / 2; - yadif->ypitch = ( yadif->ywidth + 15 ) / 16 * 16; - yadif->uvpitch = ( yadif->uvwidth + 15 ) / 16 * 16; - yadif->ysrc = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); - yadif->usrc = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch); - yadif->vsrc = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); - yadif->yprev = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); - yadif->uprev = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); - yadif->vprev = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); - yadif->ynext = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); - yadif->unext = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); - yadif->vnext = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); - yadif->ydest = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); - yadif->udest = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); - yadif->vdest = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); - + const int order = mlt_properties_get_int( properties, "top_field_first" ); + const int pitch = *width << 1; + const int parity = 0; + + // Convert packed to planar + YUY2ToPlanes( *image, pitch, *width, *height, yadif->ysrc, + yadif->ypitch, yadif->usrc, yadif->vsrc, yadif->uvpitch, yadif->cpu ); + YUY2ToPlanes( previous_image, pitch, *width, *height, yadif->yprev, + yadif->ypitch, yadif->uprev, yadif->vprev, yadif->uvpitch, yadif->cpu ); + YUY2ToPlanes( next_image, pitch, *width, *height, yadif->ynext, + yadif->ypitch, yadif->unext, yadif->vnext, yadif->uvpitch, yadif->cpu ); + + // Deinterlace each plane + filter_plane( mode, yadif->ydest, yadif->ypitch, yadif->yprev, yadif->ysrc, + yadif->ynext, yadif->ypitch, *width, *height, parity, order, yadif->cpu); + filter_plane( mode, yadif->udest, yadif->uvpitch,yadif->uprev, yadif->usrc, + yadif->unext, yadif->uvpitch, *width >> 1, *height, parity, order, yadif->cpu); + filter_plane( mode, yadif->vdest, yadif->uvpitch, yadif->vprev, yadif->vsrc, + yadif->vnext, yadif->uvpitch, *width >> 1, *height, parity, order, yadif->cpu); + + // Convert planar to packed + YUY2FromPlanes( *image, pitch, *width, *height, yadif->ydest, + yadif->ypitch, yadif->udest, yadif->vdest, yadif->uvpitch, yadif->cpu); + + close_yadif( yadif ); } - - const int order = mlt_properties_get_int( properties, "top_field_first" ); - const int pitch = *width << 1; - const int parity = 0; - - // Convert packed to planar - YUY2ToPlanes( *image, pitch, *width, *height, yadif->ysrc, - yadif->ypitch, yadif->usrc, yadif->vsrc, yadif->uvpitch, yadif->cpu ); - YUY2ToPlanes( previous_image, pitch, *width, *height, yadif->yprev, - yadif->ypitch, yadif->uprev, yadif->vprev, yadif->uvpitch, yadif->cpu ); - YUY2ToPlanes( next_image, pitch, *width, *height, yadif->ynext, - yadif->ypitch, yadif->unext, yadif->vnext, yadif->uvpitch, yadif->cpu ); - - // Deinterlace each plane - filter_plane( mode, yadif->ydest, yadif->ypitch, yadif->yprev, yadif->ysrc, - yadif->ynext, yadif->ypitch, *width, *height, parity, order, yadif->cpu); - filter_plane( mode, yadif->udest, yadif->uvpitch,yadif->uprev, yadif->usrc, - yadif->unext, yadif->uvpitch, *width >> 1, *height, parity, order, yadif->cpu); - filter_plane( mode, yadif->vdest, yadif->uvpitch, yadif->vprev, yadif->vsrc, - yadif->vnext, yadif->uvpitch, *width >> 1, *height, parity, order, yadif->cpu); - - // Convert planar to packed - YUY2FromPlanes( *image, pitch, *width, *height, yadif->ydest, - yadif->ypitch, yadif->udest, yadif->vdest, yadif->uvpitch, yadif->cpu); } } } + else + { + // Get the current frame's image + error = mlt_frame_get_image( frame, image, format, width, height, 0 ); + } return error; } /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +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_properties properties = MLT_FRAME_PROPERTIES( this ); + mlt_filter filter = mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int deinterlace = mlt_properties_get_int( properties, "consumer_deinterlace" ); + // The progressive var should only represent the frame's original state and not the state + // as modified by this filter! int progressive = mlt_properties_get_int( properties, "progressive" ); - - // Pop the service off the stack - mlt_filter filter = mlt_frame_pop_service( this ); + // At this point - before image was requested - (progressive == 0) cannot be trusted because + // some producers (avformat) do not yet know. - // Get the input image - if ( deinterlace && !progressive ) + if ( deinterlace && !mlt_properties_get_int( properties, "test_image" ) ) { // Determine deinterlace method char *method_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "method" ); @@ -159,53 +220,76 @@ static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format * else if ( strcmp( method_str, "greedy" ) == 0 ) method = DEINTERLACE_GREEDY; - *format = mlt_image_yuv422; + // Some producers like pixbuf want rescale_width & _height, but will not get them if you request + // the previous image first. So, on the first iteration, we use linearblend. + if ( ( method == DEINTERLACE_YADIF || method == DEINTERLACE_YADIF_NOSPATIAL ) && + !mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "_notfirst" ) ) + { + mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "_notfirst", 1 ); + method = DEINTERLACE_LINEARBLEND; + error = 1; + } if ( method == DEINTERLACE_YADIF ) { - int mode = 0; - error = deinterlace_yadif( this, filter, image, format, width, height, mode ); - progressive = mlt_properties_get_int( properties, "progressive" ); + error = deinterlace_yadif( frame, filter, image, format, width, height, YADIF_MODE_TEMPORAL_SPATIAL ); } else if ( method == DEINTERLACE_YADIF_NOSPATIAL ) { - int mode = 2; - error = deinterlace_yadif( this, filter, image, format, width, height, mode ); - progressive = mlt_properties_get_int( properties, "progressive" ); + error = deinterlace_yadif( frame, filter, image, format, width, height, YADIF_MODE_TEMPORAL ); } if ( error || ( method > DEINTERLACE_NONE && method < DEINTERLACE_YADIF ) ) { - // Signal that we no longer need previous and next frames mlt_service service = mlt_properties_get_data( MLT_FILTER_PROPERTIES(filter), "service", NULL ); - mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 0 ); - - if ( error ) - method = DEINTERLACE_ONEFIELD; - + // Get the current frame's image - error = mlt_frame_get_image( this, image, format, width, height, writable ); + int error2 = mlt_frame_get_image( frame, image, format, width, height, writable ); progressive = mlt_properties_get_int( properties, "progressive" ); - // Check that we aren't already progressive - if ( !progressive && !error && *image && *format == mlt_image_yuv422 ) + if ( error ) + { + method = DEINTERLACE_LINEARBLEND; + // If YADIF requested, prev/next cancelled because some previous frames were progressive, + // but new frames are interlaced, then turn prev/next frames back on. + if ( !progressive ) + mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 1 ); + } + else { - // Deinterlace the image using one of the Xine deinterlacers - int image_size = *width * *height * 2; - uint8_t *new_image = mlt_pool_alloc( image_size ); + // Signal that we no longer need previous and next frames + mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 0 ); + } + error = error2; + + if ( !error && !progressive ) + { + // OK, now we know we have work to do and can request the image in our format + error = frame->convert_image( frame, image, format, mlt_image_yuv422 ); + + // Check that we aren't already progressive + if ( !error && *image && *format == mlt_image_yuv422 ) + { + // Deinterlace the image using one of the Xine deinterlacers + int image_size = *width * *height * 2; + uint8_t *new_image = mlt_pool_alloc( image_size ); - deinterlace_yuv( new_image, image, *width * 2, *height, method ); - mlt_properties_set_data( properties, "image", new_image, image_size, mlt_pool_release, NULL ); - *image = new_image; + deinterlace_yuv( new_image, image, *width * 2, *height, method ); + mlt_frame_set_image( frame, new_image, image_size, mlt_pool_release ); + *image = new_image; + } } } else if ( method == DEINTERLACE_NONE ) { - error = mlt_frame_get_image( this, image, format, width, height, writable ); + error = mlt_frame_get_image( frame, image, format, width, height, writable ); } - + + // update progressive flag after having obtained image + progressive = mlt_properties_get_int( properties, "progressive" ); + mlt_log_debug( MLT_FILTER_SERVICE( filter ), "error %d deint %d prog %d fmt %s method %s\n", error, deinterlace, progressive, mlt_image_format_name( *format ), method_str ? method_str : "yadif" ); - + if ( !error ) { // Make sure that others know the frame is deinterlaced @@ -215,14 +299,15 @@ static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format * else { // Pass through - error = mlt_frame_get_image( this, image, format, width, height, writable ); + error = mlt_frame_get_image( frame, image, format, width, height, writable ); } if ( !deinterlace || progressive ) { // Signal that we no longer need previous and next frames mlt_service service = mlt_properties_get_data( MLT_FILTER_PROPERTIES(filter), "service", NULL ); - mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 0 ); + if ( service ) + mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 0 ); } return error; @@ -231,10 +316,10 @@ static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format * /** Deinterlace filter processing - this should be lazy evaluation here... */ -static mlt_frame deinterlace_process( mlt_filter this, mlt_frame frame ) +static mlt_frame deinterlace_process( mlt_filter filter, mlt_frame frame ) { - // Push this on to the service stack - mlt_frame_push_service( frame, this ); + // Push filter on to the service stack + mlt_frame_push_service( frame, filter ); // Push the get_image method on to the stack mlt_frame_push_get_image( frame, filter_get_image ); @@ -242,30 +327,6 @@ static mlt_frame deinterlace_process( mlt_filter this, mlt_frame frame ) return frame; } -static void filter_close( mlt_filter this ) -{ - yadif_filter *yadif = mlt_properties_get_data( MLT_FILTER_PROPERTIES( this ), "yadif", NULL ); - if ( yadif ) - { - if ( yadif->ysrc ) - { - mlt_pool_release( yadif->ysrc ); - mlt_pool_release( yadif->usrc ); - mlt_pool_release( yadif->vsrc ); - mlt_pool_release( yadif->yprev ); - mlt_pool_release( yadif->uprev ); - mlt_pool_release( yadif->vprev ); - mlt_pool_release( yadif->ynext ); - mlt_pool_release( yadif->unext ); - mlt_pool_release( yadif->vnext ); - mlt_pool_release( yadif->ydest ); - mlt_pool_release( yadif->udest ); - mlt_pool_release( yadif->vdest ); - } - mlt_pool_release( yadif ); - } -} - static void on_service_changed( mlt_service owner, mlt_service filter ) { mlt_service service = mlt_properties_get_data( MLT_SERVICE_PROPERTIES(filter), "service", NULL ); @@ -277,41 +338,13 @@ static void on_service_changed( mlt_service owner, mlt_service filter ) mlt_filter filter_deinterlace_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - yadif_filter *yadif = mlt_pool_alloc( sizeof( *yadif ) ); - - yadif->cpu = 0; // Pure C -#ifdef USE_SSE - yadif->cpu |= AVS_CPU_INTEGER_SSE; -#endif -#ifdef USE_SSE2 - yadif->cpu |= AVS_CPU_SSE2; -#endif - yadif->ysrc = NULL; - this->process = deinterlace_process; - this->close = filter_close; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "method", arg ); - mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "yadif", yadif, sizeof(*yadif), NULL, NULL ); - mlt_events_listen( MLT_FILTER_PROPERTIES( this ), this, "service-changed", (mlt_listener) on_service_changed ); - -#if defined(__GNUC__) && !defined(PIC) - // Set SSSE3 bit to cpu - asm (\ - "mov $1, %%eax \n\t"\ - "push %%ebx \n\t"\ - "cpuid \n\t"\ - "pop %%ebx \n\t"\ - "mov %%ecx, %%edx \n\t"\ - "shr $9, %%edx \n\t"\ - "and $1, %%edx \n\t"\ - "shl $9, %%edx \n\t"\ - "and $511, %%ebx \n\t"\ - "or %%edx, %%ebx \n\t"\ - : "=b"(yadif->cpu) : "p"(yadif->cpu) : "%eax", "%ecx", "%edx"); -#endif + filter->process = deinterlace_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "method", arg ); + mlt_events_listen( MLT_FILTER_PROPERTIES( filter ), filter, "service-changed", (mlt_listener) on_service_changed ); } - return this; + return filter; }