diff options
-rw-r--r-- | include/xine.h.in | 24 | ||||
-rw-r--r-- | src/video_out/video_out_vdpau.c | 168 | ||||
-rw-r--r-- | src/xine-engine/osd.c | 173 | ||||
-rw-r--r-- | src/xine-engine/osd.h | 25 | ||||
-rw-r--r-- | src/xine-engine/video_out.h | 24 | ||||
-rw-r--r-- | src/xine-engine/xine.c | 48 | ||||
-rw-r--r-- | src/xine-engine/xine_interface.c | 8 |
7 files changed, 369 insertions, 101 deletions
diff --git a/include/xine.h.in b/include/xine.h.in index f66ed0d3e..e27a58e29 100644 --- a/include/xine.h.in +++ b/include/xine.h.in @@ -2134,8 +2134,10 @@ void xine_event_send (xine_stream_t *stream, const xine_event_t *event) XINE_PRO /* yellow text, black border, transparent background */ #define XINE_TEXTPALETTE_YELLOW_BLACK_TRANSPARENT 3 -#define XINE_OSD_CAP_FREETYPE2 0x0001 /* freetype2 support compiled in */ -#define XINE_OSD_CAP_UNSCALED 0x0002 /* unscaled overlays supp. by vo drv */ +#define XINE_OSD_CAP_FREETYPE2 0x0001 /* freetype2 support compiled in */ +#define XINE_OSD_CAP_UNSCALED 0x0002 /* unscaled overlays supp. by vo drv */ +#define XINE_OSD_CAP_CUSTOM_EXTENT 0x0004 /* hardware scaled to match video output window */ +#define XINE_OSD_CAP_ARGB_LAYER 0x0008 /* supports separate true color layer */ typedef struct xine_osd_s xine_osd_t; @@ -2203,10 +2205,24 @@ void xine_osd_set_palette (xine_osd_t *self, /* * set an argb buffer to be blended into video - * this is currently only supported with vdpau + * the buffer must exactly match the osd dimensions + * and stay valid while the osd is on screen. pass + * a NULL pointer to safely remove the buffer from + * the osd layer. only the dirty area will be + * updated on screen. for convinience the whole + * osd object will be considered dirty when setting + * a different buffer pointer. + * see also XINE_OSD_CAP_ARGB_LAYER */ void xine_osd_set_argb_buffer(xine_osd_t *self, uint32_t *argb_buffer, - int x, int y, int width, int height) XINE_PROTECTED; + int dirty_x, int dirty_y, int dirty_width, int dirty_height) XINE_PROTECTED; + +/* + * define extent of reference coordinate system + * for video resolution independent osds. + * see also XINE_OSD_CAP_CUSTOM_EXTENT + */ +void xine_osd_set_extent(xine_osd_t *self, int extent_width, int extent_height) XINE_PROTECTED; /* * close osd rendering engine diff --git a/src/video_out/video_out_vdpau.c b/src/video_out/video_out_vdpau.c index 857076869..e9f561579 100644 --- a/src/video_out/video_out_vdpau.c +++ b/src/video_out/video_out_vdpau.c @@ -216,6 +216,10 @@ typedef struct { uint32_t argb_overlay_width; uint32_t argb_overlay_height; int has_argb_overlay; + int argb_osd_x; + int argb_osd_y; + int argb_osd_w; + int argb_osd_h; VdpVideoSurface soft_surface; uint32_t soft_surface_width; @@ -280,33 +284,78 @@ static void vdpau_overlay_clut_yuv2rgb(vdpau_driver_t *this, vo_overlay_t *over -static int vdpau_process_argb_ovl( vdpau_driver_t *this_gen, vo_overlay_t *overlay ) +static int vdpau_process_argb_ovl( vdpau_driver_t *this_gen, vo_frame_t *frame_gen, vo_overlay_t *overlay ) { vdpau_driver_t *this = (vdpau_driver_t *) this_gen; - if(overlay->argb_buffer == NULL) + if(overlay->argb_layer == NULL) return 0; - if ( (this->argb_overlay_width < overlay->width ) || (this->argb_overlay_height < overlay->height) || (this->argb_overlay == VDP_INVALID_HANDLE) ) { - if (this->argb_overlay != VDP_INVALID_HANDLE) { - vdp_output_surface_destroy( this->argb_overlay ); + pthread_mutex_lock(&overlay->argb_layer->mutex); + + if (overlay->argb_layer->buffer != NULL) { + int extent_width = overlay->extent_width; + int extent_height = overlay->extent_height; + if (extent_width <= 0 || extent_height <= 0) { + extent_width = frame_gen->width; + extent_height = frame_gen->height; } - VdpStatus st = vdp_output_surface_create( vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, overlay->width, overlay->height, &this->argb_overlay ); - if ( st != VDP_STATUS_OK ) { - printf( "vdpau_process_argb_ovl: vdp_output_surface_create failed : %s\n", vdp_get_error_string(st) ); + + if (extent_width > 0 && extent_height > 0) { + if ( (this->argb_overlay_width != extent_width ) || (this->argb_overlay_height != extent_height) || (this->argb_overlay == VDP_INVALID_HANDLE) ) { + if (this->argb_overlay != VDP_INVALID_HANDLE) { + vdp_output_surface_destroy( this->argb_overlay ); + } + VdpStatus st = vdp_output_surface_create( vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, extent_width, extent_height, &this->argb_overlay ); + if ( st != VDP_STATUS_OK ) { + printf( "vdpau_process_argb_ovl: vdp_output_surface_create failed : %s\n", vdp_get_error_string(st) ); + } + this->argb_overlay_width = extent_width; + this->argb_overlay_height = extent_height; + + /* set stored osd location to extent as any smaller osd requires to clear the surface first */ + this->argb_osd_x = 0; + this->argb_osd_y = 0; + this->argb_osd_w = extent_width; + this->argb_osd_h = extent_height; + } + + /* wipe surface if osd layout changed */ + if (overlay->x != this->argb_osd_x || overlay->y != this->argb_osd_y || overlay->width != this->argb_osd_w || overlay->height != this->argb_osd_h) { + this->argb_osd_x = overlay->x; + this->argb_osd_y = overlay->y; + this->argb_osd_w = overlay->width; + this->argb_osd_h = overlay->height; + + uint32_t *zeros = calloc(4 * extent_width, extent_height); + if (zeros) { + uint32_t pitch = extent_width * 4; + VdpRect dest = { 0, 0, extent_width, extent_height }; + VdpStatus st = vdp_output_surface_put_bits( this->argb_overlay, (void*)&(zeros), &pitch, &dest ); + if ( st != VDP_STATUS_OK ) + printf( "vdpau_process_argb_ovl: vdp_output_surface_put_bits_native failed : %s\n", vdp_get_error_string(st) ); + free(zeros); + } + } + + /* set destination area according to dirty area of argb layer and reset dirty area */ + uint32_t pitch = overlay->width * 4; + uint32_t *buffer_start = overlay->argb_layer->buffer + overlay->argb_layer->y1 * overlay->width + overlay->argb_layer->x1; + VdpRect dest = { overlay->x + overlay->argb_layer->x1, overlay->y + overlay->argb_layer->y1, overlay->x + overlay->argb_layer->x2, overlay->y + overlay->argb_layer->y2 }; + overlay->argb_layer->x1 = overlay->width; + overlay->argb_layer->y1 = overlay->height; + overlay->argb_layer->x2 = 0; + overlay->argb_layer->y2 = 0; + + VdpStatus st = vdp_output_surface_put_bits( this->argb_overlay, (void*)&(buffer_start), &pitch, &dest ); + if ( st != VDP_STATUS_OK ) { + printf( "vdpau_process_argb_ovl: vdp_output_surface_put_bits_native failed : %s\n", vdp_get_error_string(st) ); + } else + this->has_argb_overlay = 1; } - this->argb_overlay_width = overlay->width; - this->argb_overlay_height = overlay->height; } - uint32_t pitch = this->argb_overlay_width*4; - VdpRect dest = { 0, overlay->y, this->argb_overlay_width, overlay->y + overlay->height }; - uint32_t *buffer_start = overlay->argb_buffer + overlay->y*this->argb_overlay_width; - VdpStatus st = vdp_output_surface_put_bits( this->argb_overlay, (void*)&(buffer_start), &pitch, &dest ); - if ( st != VDP_STATUS_OK ) { - printf( "vdpau_process_argb_ovl: vdp_output_surface_put_bits_native failed : %s\n", vdp_get_error_string(st) ); - } else - this->has_argb_overlay = 1; + pthread_mutex_unlock(&overlay->argb_layer->mutex); return 1; } @@ -418,8 +467,8 @@ static void vdpau_overlay_blend (vo_driver_t *this_gen, vo_frame_t *frame_gen, v ++this->ovl_changed; } - if(overlay->argb_buffer) - vdpau_process_argb_ovl( this, overlay ); + if(overlay->argb_layer) + vdpau_process_argb_ovl( this, frame_gen, overlay ); } @@ -603,6 +652,81 @@ static vo_frame_t *vdpau_alloc_frame (vo_driver_t *this_gen) +static void vdpau_provide_standard_frame_data (vo_frame_t *this_gen, xine_current_frame_data_t *data) +{ + vdpau_frame_t *this = (vdpau_frame_t *)this_gen; + VdpStatus st; + VdpYCbCrFormat format; + + if (this->vo_frame.format != XINE_IMGFMT_VDPAU) { + fprintf(stderr, "vdpau_provide_standard_frame_data: unexpected frame format 0x%08x!\n", this->vo_frame.format); + return; + } + + if (!(this->flags & VO_CHROMA_422)) { + data->format = XINE_IMGFMT_YV12; + data->img_size = this->vo_frame.width * this->vo_frame.height + + ((this->vo_frame.width + 1) / 2) * ((this->vo_frame.height + 1) / 2) + + ((this->vo_frame.width + 1) / 2) * ((this->vo_frame.height + 1) / 2); + if (data->img) { + this->vo_frame.pitches[0] = 8*((this->vo_frame.width + 7) / 8); + this->vo_frame.pitches[1] = 8*((this->vo_frame.width + 15) / 16); + this->vo_frame.pitches[2] = 8*((this->vo_frame.width + 15) / 16); + this->vo_frame.base[0] = xine_xmalloc_aligned(16, this->vo_frame.pitches[0] * this->vo_frame.height, (void **)&this->chunk[0]); + this->vo_frame.base[1] = xine_xmalloc_aligned(16, this->vo_frame.pitches[1] * ((this->vo_frame.height+1)/2), (void **)&this->chunk[1]); + this->vo_frame.base[2] = xine_xmalloc_aligned(16, this->vo_frame.pitches[2] * ((this->vo_frame.height+1)/2), (void **)&this->chunk[2]); + format = VDP_YCBCR_FORMAT_YV12; + } + } else { + data->format = XINE_IMGFMT_YUY2; + data->img_size = this->vo_frame.width * this->vo_frame.height + + ((this->vo_frame.width + 1) / 2) * this->vo_frame.height + + ((this->vo_frame.width + 1) / 2) * this->vo_frame.height; + if (data->img) { + this->vo_frame.pitches[0] = 8*((this->vo_frame.width + 3) / 4); + this->vo_frame.base[0] = xine_xmalloc_aligned(16, this->vo_frame.pitches[0] * this->vo_frame.height, (void **)&this->chunk[0]); + format = VDP_YCBCR_FORMAT_YUYV; + } + } + + if (data->img) { + st = vdp_video_surface_getbits_ycbcr(this->vdpau_accel_data.surface, format, this->vo_frame.base, this->vo_frame.pitches); + if (st != VDP_STATUS_OK) + printf("vo_vdpau: failed to get surface bits !! %s\n", vdp_get_error_string(st)); + + if (format == VDP_YCBCR_FORMAT_YV12) { + yv12_to_yv12( + /* Y */ + this->vo_frame.base[0], this->vo_frame.pitches[0], + data->img, this->vo_frame.width, + /* U */ + this->vo_frame.base[1], this->vo_frame.pitches[1], + data->img+this->vo_frame.width*this->vo_frame.height, this->vo_frame.width/2, + /* V */ + this->vo_frame.base[2], this->vo_frame.pitches[2], + data->img+this->vo_frame.width*this->vo_frame.height+this->vo_frame.width*this->vo_frame.height/4, this->vo_frame.width/2, + /* width x height */ + this->vo_frame.width, this->vo_frame.height); + } else { + yuy2_to_yuy2( + /* src */ + this->vo_frame.base[0], this->vo_frame.pitches[0], + /* dst */ + data->img, this->vo_frame.width*2, + /* width x height */ + this->vo_frame.width, this->vo_frame.height); + } + + if (this->chunk[0]) + free(this->chunk[0]); + if (this->chunk[1]) + free(this->chunk[1]); + if (this->chunk[2]) + free(this->chunk[2]); + this->chunk[0] = this->chunk[1] = this->chunk[2] = NULL; + } +} + static void vdpau_duplicate_frame_data (vo_frame_t *this_gen, vo_frame_t *original) { vdpau_frame_t *this = (vdpau_frame_t *)this_gen; @@ -690,6 +814,7 @@ static void vdpau_update_frame_format (vo_driver_t *this_gen, vo_frame_t *frame_ frame->vdpau_accel_data.surface = VDP_INVALID_HANDLE; --this->allocated_surfaces; frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; } } @@ -701,6 +826,7 @@ static void vdpau_update_frame_format (vo_driver_t *this_gen, vo_frame_t *frame_ frame->vdpau_accel_data.chroma = chroma; ++this->allocated_surfaces; frame->vo_frame.proc_duplicate_frame_data = vdpau_duplicate_frame_data; + frame->vo_frame.proc_provide_standard_frame_data = vdpau_provide_standard_frame_data; } } @@ -1504,7 +1630,7 @@ static vo_driver_t *vdpau_open_plugin (video_driver_class_t *class_gen, const vo return NULL; } - this->capabilities = VO_CAP_YV12 | VO_CAP_YUY2 | VO_CAP_CROP | VO_CAP_UNSCALED_OVERLAY; + this->capabilities = VO_CAP_YV12 | VO_CAP_YUY2 | VO_CAP_CROP | VO_CAP_UNSCALED_OVERLAY | VO_CAP_CUSTOM_EXTENT_OVERLAY | VO_CAP_ARGB_LAYER_OVERLAY; ok = 0; uint32_t mw, mh, ml, mr; st = vdp_decoder_query_capabilities( vdp_device, VDP_DECODER_PROFILE_H264_MAIN, &ok, &ml, &mr, &mw, &mh ); diff --git a/src/xine-engine/osd.c b/src/xine-engine/osd.c index 4b521e539..7f4e6aefb 100644 --- a/src/xine-engine/osd.c +++ b/src/xine-engine/osd.c @@ -143,16 +143,19 @@ static osd_object_t *XINE_MALLOC osd_new_object (osd_renderer_t *this, int width osd->next = this->osds; this->osds = osd; + osd->extent_width = 0; + osd->extent_height = 0; osd->width = width; osd->height = height; osd->area = calloc(width, height); + osd->area_touched = 0; - osd->x1 = width; - osd->y1 = height; - osd->x2 = 0; - osd->y2 = 0; + osd->x1 = osd->argb_layer.x1 = width; + osd->y1 = osd->argb_layer.y1 = height; + osd->x2 = osd->argb_layer.x2 = 0; + osd->y2 = osd->argb_layer.y2 = 0; - osd->argb_buffer = NULL; + pthread_mutex_init(&osd->argb_layer.mutex, NULL); memcpy(osd->color, textpalettes_color[0], sizeof(textpalettes_color[0])); memcpy(osd->trans, textpalettes_trans[0], sizeof(textpalettes_trans[0])); @@ -171,6 +174,18 @@ static osd_object_t *XINE_MALLOC osd_new_object (osd_renderer_t *this, int width return osd; } +/* + * osd extent must be set to achive video resolution independent osds + * both sizes must be > 0 to take effect. otherwise, video resolution + * will still be used. the extent defines the reference coordinate + * system which is matched to the video output area. + */ +static void osd_set_extent (osd_object_t *osd, int extent_width, int extent_height) { + + osd->extent_width = extent_width; + osd->extent_height = extent_height; +} + /* @@ -231,7 +246,7 @@ static int _osd_show (osd_object_t *osd, int64_t vpts, int unscaled ) { memset( this->event.object.overlay, 0, sizeof(*this->event.object.overlay) ); - this->event.object.overlay->argb_buffer = osd->argb_buffer; + this->event.object.overlay->argb_layer = &osd->argb_layer; this->event.object.overlay->unscaled = unscaled; this->event.object.overlay->x = osd->display_x + osd->x1; @@ -239,6 +254,9 @@ static int _osd_show (osd_object_t *osd, int64_t vpts, int unscaled ) { this->event.object.overlay->width = osd->x2 - osd->x1; this->event.object.overlay->height = osd->y2 - osd->y1; + this->event.object.overlay->extent_width = osd->extent_width; + this->event.object.overlay->extent_height = osd->extent_height; + this->event.object.overlay->hili_top = 0; this->event.object.overlay->hili_bottom = this->event.object.overlay->height; this->event.object.overlay->hili_left = 0; @@ -246,53 +264,59 @@ static int _osd_show (osd_object_t *osd, int64_t vpts, int unscaled ) { /* there will be at least that many rle objects (one for each row) */ this->event.object.overlay->num_rle = 0; - /* We will never need more rle objects than columns in any row - Rely on lazy page allocation to avoid us actually taking up - this much RAM */ - this->event.object.overlay->data_size = osd->width * osd->height; - rle_p = this->event.object.overlay->rle = - malloc(this->event.object.overlay->data_size * sizeof(rle_elem_t) ); + if (!osd->area_touched) { + /* avoid rle encoding when only argb_layer is modified */ + this->event.object.overlay->data_size = 0; + rle_p = this->event.object.overlay->rle = NULL; + } else { + /* We will never need more rle objects than columns in any row + Rely on lazy page allocation to avoid us actually taking up + this much RAM */ + this->event.object.overlay->data_size = osd->width * osd->height; + rle_p = this->event.object.overlay->rle = + malloc(this->event.object.overlay->data_size * sizeof(rle_elem_t) ); - for( y = osd->y1; y < osd->y2; y++ ) { + for( y = osd->y1; y < osd->y2; y++ ) { #ifdef DEBUG_RLE - lprintf("osd_show %p y = %d: ", osd, y); + lprintf("osd_show %p y = %d: ", osd, y); #endif - c = osd->area + y * osd->width + osd->x1; + c = osd->area + y * osd->width + osd->x1; - /* initialize a rle object with the first pixel's color */ - rle.len = 1; - rle.color = *c++; + /* initialize a rle object with the first pixel's color */ + rle.len = 1; + rle.color = *c++; - /* loop over the remaining pixels in the row */ - for( x = osd->x1 + rle.len; x < osd->x2; x++, c++ ) { - if( rle.color != *c ) { + /* loop over the remaining pixels in the row */ + for( x = osd->x1 + rle.len; x < osd->x2; x++, c++ ) { + if( rle.color != *c ) { #ifdef DEBUG_RLE - lprintf("(%d, %d), ", rle.len, rle.color); + lprintf("(%d, %d), ", rle.len, rle.color); #endif - *rle_p++ = rle; - this->event.object.overlay->num_rle++; + *rle_p++ = rle; + this->event.object.overlay->num_rle++; - rle.color = *c; - rle.len = 1; - } else { - rle.len++; + rle.color = *c; + rle.len = 1; + } else { + rle.len++; + } } - } #ifdef DEBUG_RLE - lprintf("(%d, %d)\n", rle.len, rle.color); + lprintf("(%d, %d)\n", rle.len, rle.color); #endif - *rle_p++ = rle; - this->event.object.overlay->num_rle++; - } + *rle_p++ = rle; + this->event.object.overlay->num_rle++; + } #ifdef DEBUG_RLE - lprintf("osd_show %p rle ends\n", osd); + lprintf("osd_show %p rle ends\n", osd); #endif - lprintf("num_rle = %d\n", this->event.object.overlay->num_rle); - - memcpy(this->event.object.overlay->hili_color, osd->color, sizeof(osd->color)); - memcpy(this->event.object.overlay->hili_trans, osd->trans, sizeof(osd->trans)); - memcpy(this->event.object.overlay->color, osd->color, sizeof(osd->color)); - memcpy(this->event.object.overlay->trans, osd->trans, sizeof(osd->trans)); + lprintf("num_rle = %d\n", this->event.object.overlay->num_rle); + + memcpy(this->event.object.overlay->hili_color, osd->color, sizeof(osd->color)); + memcpy(this->event.object.overlay->hili_trans, osd->trans, sizeof(osd->trans)); + memcpy(this->event.object.overlay->color, osd->color, sizeof(osd->color)); + memcpy(this->event.object.overlay->trans, osd->trans, sizeof(osd->trans)); + } this->event.event_type = OVERLAY_EVENT_SHOW; this->event.vpts = vpts; @@ -376,11 +400,14 @@ static int osd_hide (osd_object_t *osd, int64_t vpts) { static void osd_clear (osd_object_t *osd) { lprintf("osd=%p\n",osd); - memset(osd->area, 0, osd->width * osd->height); - osd->x1 = osd->width; - osd->y1 = osd->height; - osd->x2 = 0; - osd->y2 = 0; + if (osd->area_touched) { + osd->area_touched = 0; + memset(osd->area, 0, osd->width * osd->height); + } + osd->x1 = osd->argb_layer.x1 = osd->width; + osd->y1 = osd->argb_layer.y1 = osd->height; + osd->x2 = osd->argb_layer.x2 = 0; + osd->y2 = osd->argb_layer.y2 = 0; } /* @@ -402,6 +429,7 @@ static void osd_point (osd_object_t *osd, int x, int y, int color) { osd->x2 = MAX(osd->x2, (x + 1)); osd->y1 = MIN(osd->y1, y); osd->y2 = MAX(osd->y2, (y + 1)); + osd->area_touched = 1; c = osd->area + y * osd->width + x; *c = color; @@ -461,6 +489,7 @@ static void osd_line (osd_object_t *osd, osd->x2 = MAX( osd->x2, x2 ); osd->y1 = MIN( osd->y1, y1 ); osd->y2 = MAX( osd->y2, y2 ); + osd->area_touched = 1; dx = x2 - x1; dy = y2 - y1; @@ -574,6 +603,7 @@ static void osd_filled_rect (osd_object_t *osd, osd->x2 = MAX( osd->x2, dx ); osd->y1 = MIN( osd->y1, y ); osd->y2 = MAX( osd->y2, dy ); + osd->area_touched = 1; dx -= x; dy -= y; @@ -1131,6 +1161,7 @@ static int osd_render_text (osd_object_t *osd, int x1, int y1, if( x1 < osd->x1 ) osd->x1 = x1; if( y1 < osd->y1 ) osd->y1 = y1; + osd->area_touched = 1; inbuf = text; inbytesleft = strlen(text); @@ -1479,6 +1510,7 @@ static void osd_free_object (osd_object_t *osd_to_close) { else this->osds = osd->next; + pthread_mutex_destroy(&osd->argb_layer.mutex); free( osd ); break; } @@ -1524,6 +1556,7 @@ static void osd_draw_bitmap(osd_object_t *osd, uint8_t *bitmap, osd->x2 = MAX( osd->x2, x1+width ); osd->y1 = MIN( osd->y1, y1 ); osd->y2 = MAX( osd->y2, y1+height ); + osd->area_touched = 1; for( y=0; y<height; y++ ) { if ( palette_map ) { @@ -1543,33 +1576,56 @@ static void osd_draw_bitmap(osd_object_t *osd, uint8_t *bitmap, } static void osd_set_argb_buffer(osd_object_t *osd, uint32_t *argb_buffer, - int x, int y, int width, int height) + int dirty_x, int dirty_y, int dirty_width, int dirty_height) { - osd->argb_buffer = argb_buffer; - if(osd->x1 == 0 || x < osd->x1) - osd->x1 = x; - if(osd->x2 == 0 || (x + width) > osd->x2) - osd->x2 = x + width; - if(osd->y1 == 0 || y < osd->y1) - osd->y1 = y; - if(osd->y2 == 0 || (y + height) > osd->y2) - osd->y2 = y + height; + if (osd->argb_layer.buffer != argb_buffer) { + dirty_x = 0; + dirty_y = 0; + dirty_width = osd->width; + dirty_height = osd->height; + } + + /* keep osd_object clipping behavior */ + osd->x1 = MIN( osd->x1, dirty_x ); + osd->x2 = MAX( osd->x2, dirty_x + dirty_width ); + osd->y1 = MIN( osd->y1, dirty_y ); + osd->y2 = MAX( osd->y2, dirty_y + dirty_height ); + + pthread_mutex_lock(&osd->argb_layer.mutex); + + /* argb layer update area accumulation */ + osd->argb_layer.x1 = MIN( osd->argb_layer.x1, dirty_x ); + osd->argb_layer.x2 = MAX( osd->argb_layer.x2, dirty_x + dirty_width ); + osd->argb_layer.y1 = MIN( osd->argb_layer.y1, dirty_y ); + osd->argb_layer.y2 = MAX( osd->argb_layer.y2, dirty_y + dirty_height ); + + osd->argb_layer.buffer = argb_buffer; + + pthread_mutex_unlock(&osd->argb_layer.mutex); } static uint32_t osd_get_capabilities (osd_object_t *osd) { osd_renderer_t *this = osd->renderer; uint32_t capabilities = 0; + uint32_t vo_capabilities; #ifdef HAVE_FT2 capabilities |= XINE_OSD_CAP_FREETYPE2; #endif this->stream->xine->port_ticket->acquire(this->stream->xine->port_ticket, 1); - if( this->stream->video_out->get_capabilities(this->stream->video_out) & - VO_CAP_UNSCALED_OVERLAY) - capabilities |= XINE_OSD_CAP_UNSCALED; + vo_capabilities = this->stream->video_out->get_capabilities(this->stream->video_out); this->stream->xine->port_ticket->release(this->stream->xine->port_ticket, 1); + + if (vo_capabilities & VO_CAP_UNSCALED_OVERLAY) + capabilities |= XINE_OSD_CAP_UNSCALED; + + if (vo_capabilities & VO_CAP_CUSTOM_EXTENT_OVERLAY) + capabilities |= XINE_OSD_CAP_CUSTOM_EXTENT; + + if (vo_capabilities & VO_CAP_ARGB_LAYER_OVERLAY) + capabilities |= XINE_OSD_CAP_ARGB_LAYER; return capabilities; } @@ -1634,6 +1690,7 @@ osd_renderer_t *_x_osd_renderer_init( xine_stream_t *stream ) { this->set_argb_buffer = osd_set_argb_buffer; this->show_unscaled = osd_show_unscaled; this->get_capabilities = osd_get_capabilities; + this->set_extent = osd_set_extent; return this; } diff --git a/src/xine-engine/osd.h b/src/xine-engine/osd.h index a6acc11ab..36e6fc0ae 100644 --- a/src/xine-engine/osd.h +++ b/src/xine-engine/osd.h @@ -47,8 +47,12 @@ struct osd_object_s { int width, height; /* work area dimentions */ uint8_t *area; /* work area */ + int area_touched; /* work area was used for painting */ int display_x,display_y; /* where to display it in screen */ + /* extent of reference coordinate system */ + int extent_width, extent_height; + /* clipping box inside work area */ int x1, y1; int x2, y2; @@ -70,7 +74,7 @@ struct osd_object_s { /* this holds an optional ARGB overlay, which * is only be used by supported video_out modules. * right now this is only vdpau */ - uint32_t *argb_buffer; + argb_layer_t argb_layer; }; @@ -219,10 +223,25 @@ struct osd_renderer_s { uint32_t (*get_capabilities) (osd_object_t *osd); /* - * set a buffer to an argb buffer + * define extent of reference coordinate system for video + * resolution independent osds. both sizes must be > 0 to + * take effect. otherwise, video resolution will be used. + */ + void (*set_extent) (osd_object_t *osd, int extent_width, int extent_height); + + /* + * set an argb buffer to be blended into video + * the buffer must exactly match the osd dimensions + * and stay valid while the osd is on screen. pass + * a NULL pointer to safely remove the buffer from + * the osd layer. only the dirty area will be + * updated on screen. for convinience the whole + * osd object will be considered dirty when setting + * a different buffer pointer. + * see also XINE_OSD_CAP_ARGB_LAYER */ void (*set_argb_buffer) (osd_object_t *osd, uint32_t *argb_buffer, - int x, int y, int width, int height); + int dirty_x, int dirty_y, int dirty_width, int dirty_height); /* private stuff */ diff --git a/src/xine-engine/video_out.h b/src/xine-engine/video_out.h index 76b347a41..0c73ddafa 100644 --- a/src/xine-engine/video_out.h +++ b/src/xine-engine/video_out.h @@ -68,6 +68,14 @@ struct vo_frame_s { * member functions */ + /* Provide a copy of the frame's image in an image format already known to xine. data's member */ + /* have already been intialized to frame's content on entry, so it's usually only necessary to */ + /* change format and img_size. In case img is set, it will point to a memory block of suitable */ + /* size (size has been determined by a previous call with img == NULL). img content and img_size */ + /* must adhere to the specification of _x_get_current_frame_data(). */ + /* Currently this is needed for all image formats except XINE_IMGFMT_YV12 and XINE_IMGFMT_YUY2. */ + void (*proc_provide_standard_frame_data) (vo_frame_t *vo_img, xine_current_frame_data_t *data); + /* Duplicate picture data and acceleration specific data of a frame. */ /* if the image format isn't already known by Xine. Currently this is needed */ /* For all image formats except XINE_IMGFMT_YV12 and XINE_IMGFMT_YUY2 */ @@ -286,6 +294,8 @@ struct xine_video_port_s { #define VO_CAP_XXMC 0x00000040 /* driver can use extended XvMC */ #define VO_CAP_VDPAU_H264 0x00000080 /* driver can use VDPAU for H264 */ #define VO_CAP_VDPAU_MPEG12 0x00000100 /* driver can use VDPAU for mpeg1/2 */ +#define VO_CAP_CUSTOM_EXTENT_OVERLAY 0x01000000 /* driver can blend custom extent overlay to output extent */ +#define VO_CAP_ARGB_LAYER_OVERLAY 0x02000000 /* driver supports true color overlay */ /* @@ -398,6 +408,14 @@ typedef struct rle_elem_s { uint16_t color; } rle_elem_t; +typedef struct argb_layer_s { + pthread_mutex_t mutex; + uint32_t *buffer; + /* dirty area */ + int x1, y1; + int x2, y2; +} argb_layer_t; + struct vo_overlay_s { rle_elem_t *rle; /* rle code buffer */ @@ -408,6 +426,10 @@ struct vo_overlay_s { int width; /* width of subpicture area */ int height; /* height of subpicture area */ + /* extent of reference coordinate system */ + int extent_width; + int extent_height; + uint32_t color[OVL_PALETTE_SIZE]; /* color lookup table */ uint8_t trans[OVL_PALETTE_SIZE]; /* mixer key table */ int rgb_clut; /* true if clut was converted to rgb */ @@ -423,7 +445,7 @@ struct vo_overlay_s { int unscaled; /* true if it should be blended unscaled */ - uint32_t *argb_buffer; + argb_layer_t *argb_layer; }; diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c index e4e36527f..e0c98c018 100644 --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.c @@ -1963,6 +1963,8 @@ static int _x_get_current_frame_data (xine_stream_t *stream, stream->xine->port_ticket->acquire(stream->xine->port_ticket, 0); frame = stream->video_out->get_last_frame (stream->video_out); + if (frame) + frame->lock(frame); stream->xine->port_ticket->release(stream->xine->port_ticket, 0); if (!frame) { @@ -1994,6 +1996,30 @@ static int _x_get_current_frame_data (xine_stream_t *stream, switch (frame->format) { + default: + if (frame->proc_provide_standard_frame_data) { + uint8_t *img = data->img; + size_t img_size = data->img_size; + data->img = 0; + data->img_size = 0; + + /* ask frame implementation for required img buffer size */ + frame->proc_provide_standard_frame_data(frame, data); + required_size = data->img_size; + + data->img = img; + data->img_size = img_size; + break; + } + + if (!data->img && !(flags & XINE_FRAME_DATA_ALLOCATE_IMG)) + break; /* not interested in image data */ + + xprintf (stream->xine, XINE_VERBOSITY_DEBUG, + "xine: error, snapshot function not implemented for format 0x%x\n", frame->format); + /* fall though and provide "green" YV12 image */ + data->format = XINE_IMGFMT_YV12; + case XINE_IMGFMT_YV12: required_size = frame->width * frame->height + ((frame->width + 1) / 2) * ((frame->height + 1) / 2) @@ -2006,26 +2032,21 @@ static int _x_get_current_frame_data (xine_stream_t *stream, + ((frame->width + 1) / 2) * frame->height; break; - default: - if (data->img || (flags & XINE_FRAME_DATA_ALLOCATE_IMG)) { - xprintf (stream->xine, XINE_VERBOSITY_DEBUG, - "xine: error, snapshot function not implemented for format 0x%x\n", frame->format); - _x_abort (); - } - - required_size = 0; } if (flags & XINE_FRAME_DATA_ALLOCATE_IMG) { /* return allocated buffer size */ data->img_size = required_size; /* allocate img or fail */ - if (!(data->img = calloc(1, required_size))) + if (!(data->img = calloc(1, required_size))) { + frame->free(frame); return 0; + } } else { /* fail if supplied buffer is to small */ if (data->img && !img_size_unknown && data->img_size < required_size) { data->img_size = required_size; + frame->free(frame); return 0; } /* return used buffer size */ @@ -2061,11 +2082,14 @@ static int _x_get_current_frame_data (xine_stream_t *stream, break; default: - xprintf (stream->xine, XINE_VERBOSITY_DEBUG, - "xine: error, snapshot function not implemented for format 0x%x\n", frame->format); - _x_abort (); + if (frame->proc_provide_standard_frame_data) + frame->proc_provide_standard_frame_data(frame, data); + else if (!(flags & XINE_FRAME_DATA_ALLOCATE_IMG)) + memset(data->img, 0, data->img_size); } } + + frame->free(frame); return 1; } diff --git a/src/xine-engine/xine_interface.c b/src/xine-engine/xine_interface.c index 4f59785a1..08ff4dc22 100644 --- a/src/xine-engine/xine_interface.c +++ b/src/xine-engine/xine_interface.c @@ -856,8 +856,12 @@ void xine_osd_draw_bitmap(xine_osd_t *this, uint8_t *bitmap, } void xine_osd_set_argb_buffer(xine_osd_t *this, uint32_t *argb_buffer, - int x, int y, int width, int height) { - this->osd.renderer->set_argb_buffer(&this->osd, argb_buffer, x, y, width, height); + int dirty_x, int dirty_y, int dirty_width, int dirty_height) { + this->osd.renderer->set_argb_buffer(&this->osd, argb_buffer, dirty_x, dirty_y, dirty_width, dirty_height); +} + +void xine_osd_set_extent(xine_osd_t *this, int extent_width, int extent_height) { + this->osd.renderer->set_extent(&this->osd, extent_width, extent_height); } |