diff options
| author | Andreas Auras <yak54@inkennet.de> | 2011-03-29 11:35:31 +0200 |
|---|---|---|
| committer | Andreas Auras <yak54@inkennet.de> | 2011-03-29 11:35:31 +0200 |
| commit | 6fa5ad78275f7f94565df3aaf3ef9bcf52c790fa (patch) | |
| tree | c03974a79c219671585942ffda661071793e35dd /src/video_out | |
| parent | 3af2095a8b756b747db80ca9e0a75f07b3cf5e25 (diff) | |
| download | xine-lib-6fa5ad78275f7f94565df3aaf3ef9bcf52c790fa.tar.gz xine-lib-6fa5ad78275f7f94565df3aaf3ef9bcf52c790fa.tar.bz2 | |
Continuous video frame grabbing feature.
In opposite to the 'xine_get_current_frame' based snapshot function this grabbing
feature allow continuous grabbing of last or next displayed video frame.
Grabbed video frames are returned in simple three byte RGB format.
Depending on the capabilities of the used video output driver video image data is
taken as close as possible at the end of the video processing chain. Thus a returned
video image could contain the blended OSD data, is deinterlaced, cropped and scaled
and video properties like hue, sat could be applied.
With this patch such a decent grabbing feature is implemented for vdpau video out driver.
If a video output driver does not have a decent grabbing implementation then there
is a generic fallback feature that grabs the video frame as they are taken from the video
display queue (like the xine_get_current_frame' function).
In this case color correct conversation to a RGB image incorporating source cropping and
scaling to the requested grab size is also supported.
A more detailed description can be found in file "xine.h".
Diffstat (limited to 'src/video_out')
| -rw-r--r-- | src/video_out/video_out_vdpau.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/src/video_out/video_out_vdpau.c b/src/video_out/video_out_vdpau.c index 3bb2bd652..69dcbae9e 100644 --- a/src/video_out/video_out_vdpau.c +++ b/src/video_out/video_out_vdpau.c @@ -134,7 +134,9 @@ VdpVideoSurfaceGetParameters *vdp_video_surface_get_parameters; VdpOutputSurfaceCreate *vdp_output_surface_create; VdpOutputSurfaceDestroy *vdp_output_surface_destroy; VdpOutputSurfaceRenderBitmapSurface *vdp_output_surface_render_bitmap_surface; +VdpOutputSurfaceRenderOutputSurface *vdp_output_surface_render_output_surface; VdpOutputSurfacePutBitsNative *vdp_output_surface_put_bits; +VdpOutputSurfaceGetBitsNative *vdp_output_surface_get_bits; VdpVideoMixerCreate *vdp_video_mixer_create; VdpVideoMixerDestroy *vdp_video_mixer_destroy; @@ -291,6 +293,17 @@ static VdpStatus guarded_vdp_decoder_render(VdpDecoder decoder, VdpVideoSurface typedef struct { + xine_grab_video_frame_t grab_frame; + + vo_driver_t *vo_driver; + VdpOutputSurface render_surface; + int vdp_runtime_nr; + int width, height; + uint32_t *rgba; +} vdpau_grab_video_frame_t; + + +typedef struct { VdpBitmapSurface ovl_bitmap; uint32_t bitmap_width, bitmap_height; int ovl_w, ovl_h; /* overlay's width and height */ @@ -375,6 +388,10 @@ typedef struct { uint8_t init_queue; uint8_t queue_length; + vdpau_grab_video_frame_t *pending_grab_request; + pthread_mutex_t grab_lock; + pthread_cond_t grab_cond; + VdpVideoMixer video_mixer; VdpChromaType video_mixer_chroma; uint32_t video_mixer_width; @@ -1588,6 +1605,117 @@ static void vdpau_check_output_size( vo_driver_t *this_gen ) } +static void vdpau_grab_current_output_surface (vdpau_driver_t *this, int64_t vpts) +{ + pthread_mutex_lock(&this->grab_lock); + + vdpau_grab_video_frame_t *frame = this->pending_grab_request; + if (frame) { + VdpStatus st; + + this->pending_grab_request = NULL; + frame->grab_frame.vpts = -1; + + VdpOutputSurface grab_surface = this->output_surface[this->current_output_surface]; + int width = this->output_surface_width[this->current_output_surface]; + int height = this->output_surface_height[this->current_output_surface]; + + /* take cropping parameters into account */ + width = width - frame->grab_frame.crop_left - frame->grab_frame.crop_right; + height = height - frame->grab_frame.crop_top - frame->grab_frame.crop_bottom; + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + /* if caller does not specify frame size we return the actual size of grabbed frame */ + if (frame->grab_frame.width <= 0) + frame->grab_frame.width = width; + if (frame->grab_frame.height <= 0) + frame->grab_frame.height = height; + + if (frame->vdp_runtime_nr != this->vdp_runtime_nr) + frame->render_surface = VDP_INVALID_HANDLE; + + if (frame->grab_frame.width != frame->width || frame->grab_frame.height != frame->height) { + free(frame->rgba); + free(frame->grab_frame.img); + frame->rgba = NULL; + frame->grab_frame.img = NULL; + + if (frame->render_surface != VDP_INVALID_HANDLE) { + st = vdp_output_surface_destroy(frame->render_surface); + frame->render_surface = VDP_INVALID_HANDLE; + if (st != VDP_STATUS_OK) { + fprintf(stderr, "vo_vdpau: Can't destroy grab render output surface: %s\n", vdp_get_error_string (st)); + pthread_cond_broadcast(&this->grab_cond); + pthread_mutex_unlock(&this->grab_lock); + return; + } + } + + frame->width = frame->grab_frame.width; + frame->height = frame->grab_frame.height; + } + + if (frame->rgba == NULL) { + frame->rgba = (uint32_t *) calloc(frame->width * frame->height, sizeof(uint32_t)); + if (frame->rgba == NULL) { + pthread_cond_broadcast(&this->grab_cond); + pthread_mutex_unlock(&this->grab_lock); + return; + } + } + if (frame->grab_frame.img == NULL) { + frame->grab_frame.img = (uint8_t *) calloc(frame->width * frame->height, 3); + if (frame->grab_frame.img == NULL) { + pthread_cond_broadcast(&this->grab_cond); + pthread_mutex_unlock(&this->grab_lock); + return; + } + } + + uint32_t pitches = frame->width * sizeof(uint32_t); + VdpRect src_rect = { frame->grab_frame.crop_left, frame->grab_frame.crop_top, width+frame->grab_frame.crop_left, height+frame->grab_frame.crop_top }; + + if (frame->width != width || frame->height != height) { + st = VDP_STATUS_OK; + if (frame->render_surface == VDP_INVALID_HANDLE) { + frame->vdp_runtime_nr = this->vdp_runtime_nr; + st = vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, frame->width, frame->height, &frame->render_surface); + } + if (st == VDP_STATUS_OK) { + st = vdp_output_surface_render_output_surface(frame->render_surface, NULL, grab_surface, &src_rect, NULL, NULL, VDP_OUTPUT_SURFACE_RENDER_ROTATE_0); + if (st == VDP_STATUS_OK) { + st = vdp_output_surface_get_bits(frame->render_surface, NULL, &frame->rgba, &pitches); + if (st == VDP_STATUS_OK) { + if (!(frame->grab_frame.flags & XINE_GRAB_VIDEO_FRAME_FLAGS_CONTINUOUS)) { + st = vdp_output_surface_destroy(frame->render_surface); + if (st != VDP_STATUS_OK) + fprintf(stderr, "vo_vdpau: Can't destroy grab render output surface: %s\n", vdp_get_error_string (st)); + frame->render_surface = VDP_INVALID_HANDLE; + } + } else + fprintf(stderr, "vo_vdpau: Can't get output surface bits for raw frame grabbing: %s\n", vdp_get_error_string (st)); + } else + fprintf(stderr, "vo_vdpau: Can't render output surface for raw frame grabbing: %s\n", vdp_get_error_string (st)); + } else + fprintf(stderr, "vo_vdpau: Can't create render output surface for raw frame grabbing: %s\n", vdp_get_error_string (st)); + } else { + st = vdp_output_surface_get_bits(grab_surface, &src_rect, &frame->rgba, &pitches); + if (st != VDP_STATUS_OK) + fprintf(stderr, "vo_vdpau: Can't get output surface bits for raw frame grabbing: %s\n", vdp_get_error_string (st)); + } + + if (st == VDP_STATUS_OK) + frame->grab_frame.vpts = vpts; + + pthread_cond_broadcast(&this->grab_cond); + } + + pthread_mutex_unlock(&this->grab_lock); +} + static void vdpau_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) { @@ -1811,6 +1939,7 @@ static void vdpau_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) if ( st != VDP_STATUS_OK ) fprintf(stderr, "vo_vdpau: vdp_video_mixer_render error : %s\n", vdp_get_error_string( st ) ); + vdpau_grab_current_output_surface( this, frame->vo_frame.vpts ); vdp_queue_get_time( vdp_queue, ¤t_time ); vdp_queue_display( vdp_queue, this->output_surface[this->current_output_surface], 0, 0, 0 ); /* display _now_ */ vdpau_shift_queue( this_gen ); @@ -1861,6 +1990,7 @@ static void vdpau_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) if ( st != VDP_STATUS_OK ) fprintf(stderr, "vo_vdpau: vdp_video_mixer_render error : %s\n", vdp_get_error_string( st ) ); + vdpau_grab_current_output_surface( this, frame->vo_frame.vpts ); vdp_queue_display( vdp_queue, this->output_surface[this->current_output_surface], 0, 0, 0 ); vdpau_shift_queue( this_gen ); } @@ -1990,6 +2120,101 @@ static void vdpau_get_property_min_max (vo_driver_t *this_gen, int property, int } +/* + * functions for grabbing RGB images from displayed frames + */ +static void vdpau_dispose_grab_video_frame(xine_grab_video_frame_t *frame_gen) +{ + vdpau_grab_video_frame_t *frame = (vdpau_grab_video_frame_t *) frame_gen; + vdpau_driver_t *this = (vdpau_driver_t *) frame->vo_driver; + + free(frame->grab_frame.img); + free(frame->rgba); + if (frame->render_surface != VDP_INVALID_HANDLE && frame->vdp_runtime_nr == this->vdp_runtime_nr) { + VdpStatus st; + st = vdp_output_surface_destroy(frame->render_surface); + if (st != VDP_STATUS_OK) + fprintf(stderr, "vo_vdpau: Can't destroy grab render output surface: %s\n", vdp_get_error_string (st) ); + } + free(frame); +} + +/* + * grab next displayed output surface. + * Note: This feature only supports grabbing of next displayed frame (implicit VO_GRAB_FRAME_FLAGS_WAIT_NEXT) + */ +static int vdpau_grab_grab_video_frame (xine_grab_video_frame_t *frame_gen) { + vdpau_grab_video_frame_t *frame = (vdpau_grab_video_frame_t *) frame_gen; + vdpau_driver_t *this = (vdpau_driver_t *) frame->vo_driver; + struct timeval tvnow, tvdiff, tvtimeout; + struct timespec ts; + + /* calculate absolute timeout time */ + tvdiff.tv_sec = frame->grab_frame.timeout / 1000; + tvdiff.tv_usec = frame->grab_frame.timeout % 1000; + tvdiff.tv_usec *= 1000; + gettimeofday(&tvnow, NULL); + timeradd(&tvnow, &tvdiff, &tvtimeout); + ts.tv_sec = tvtimeout.tv_sec; + ts.tv_nsec = tvtimeout.tv_usec; + ts.tv_nsec *= 1000; + + pthread_mutex_lock(&this->grab_lock); + + /* wait until other pending grab request is finished */ + while (this->pending_grab_request) { + if (pthread_cond_timedwait(&this->grab_cond, &this->grab_lock, &ts) == ETIMEDOUT) { + pthread_mutex_unlock(&this->grab_lock); + return 1; /* no frame available */ + } + } + + this->pending_grab_request = frame; + + /* wait until our request is finished */ + while (this->pending_grab_request) { + if (pthread_cond_timedwait(&this->grab_cond, &this->grab_lock, &ts) == ETIMEDOUT) { + this->pending_grab_request = NULL; + pthread_mutex_unlock(&this->grab_lock); + return 1; /* no frame available */ + } + } + + pthread_mutex_unlock(&this->grab_lock); + + if (frame->grab_frame.vpts == -1) + return -1; /* error happened */ + + /* convert ARGB image to RGB image */ + uint32_t *src = frame->rgba; + uint8_t *dst = frame->grab_frame.img; + int n = frame->width * frame->height; + while (n--) { + uint32_t rgba = *src++; + *dst++ = (uint8_t)(rgba >> 16); /*R*/ + *dst++ = (uint8_t)(rgba >> 8); /*G*/ + *dst++ = (uint8_t)(rgba); /*B*/ + } + + return 0; +} + + +static xine_grab_video_frame_t * vdpau_new_grab_video_frame(vo_driver_t *this) +{ + vdpau_grab_video_frame_t *frame = calloc(1, sizeof(vdpau_grab_video_frame_t)); + if (frame) { + frame->grab_frame.dispose = vdpau_dispose_grab_video_frame; + frame->grab_frame.grab = vdpau_grab_grab_video_frame; + frame->grab_frame.vpts = -1; + frame->grab_frame.timeout = XINE_GRAB_VIDEO_FRAME_DEFAULT_TIMEOUT; + frame->vo_driver = this; + frame->render_surface = VDP_INVALID_HANDLE; + } + + return (xine_grab_video_frame_t *) frame; +} + static int vdpau_gui_data_exchange (vo_driver_t *this_gen, int data_type, void *data) { @@ -2131,6 +2356,8 @@ static void vdpau_dispose (vo_driver_t *this_gen) if ( (vdp_device != VDP_INVALID_HANDLE) && vdp_device_destroy ) vdp_device_destroy( vdp_device ); + pthread_mutex_destroy(&this->grab_lock); + pthread_cond_destroy(&this->grab_cond); pthread_mutex_destroy(&this->drawable_lock); free (this); } @@ -2365,6 +2592,7 @@ static vo_driver_t *vdpau_open_plugin (video_driver_class_t *class_gen, const vo this->vo_driver.gui_data_exchange = vdpau_gui_data_exchange; this->vo_driver.dispose = vdpau_dispose; this->vo_driver.redraw_needed = vdpau_redraw_needed; + this->vo_driver.new_grab_video_frame = vdpau_new_grab_video_frame; this->surface_cleared_nr = 0; @@ -2481,9 +2709,15 @@ static vo_driver_t *vdpau_open_plugin (video_driver_class_t *class_gen, const vo st = vdp_get_proc_address( vdp_device, VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_BITMAP_SURFACE , (void*)&vdp_output_surface_render_bitmap_surface ); if ( vdpau_init_error( st, "Can't get OUTPUT_SURFACE_RENDER_BITMAP_SURFACE proc address !!", &this->vo_driver, 1 ) ) return NULL; + st = vdp_get_proc_address( vdp_device, VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE , (void*)&vdp_output_surface_render_output_surface ); + if ( vdpau_init_error( st, "Can't get OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE proc address !!", &this->vo_driver, 1 ) ) + return NULL; st = vdp_get_proc_address( vdp_device, VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE , (void*)&vdp_output_surface_put_bits ); if ( vdpau_init_error( st, "Can't get VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE proc address !!", &this->vo_driver, 1 ) ) return NULL; + st = vdp_get_proc_address( vdp_device, VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE , (void*)&vdp_output_surface_get_bits ); + if ( vdpau_init_error( st, "Can't get VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE proc address !!", &this->vo_driver, 1 ) ) + return NULL; st = vdp_get_proc_address( vdp_device, VDP_FUNC_ID_VIDEO_MIXER_CREATE , (void*)&vdp_video_mixer_create ); if ( vdpau_init_error( st, "Can't get VIDEO_MIXER_CREATE proc address !!", &this->vo_driver, 1 ) ) return NULL; @@ -2866,6 +3100,10 @@ static vo_driver_t *vdpau_open_plugin (video_driver_class_t *class_gen, const vo this->vdp_runtime_nr = 1; + this->pending_grab_request = NULL; + pthread_mutex_init(&this->grab_lock, NULL); + pthread_cond_init(&this->grab_cond, NULL); + return &this->vo_driver; } |
