X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ffmpeg_vdpau.c;h=cf2e83e8a3f58cacae643cf316409227f9cd2cb3;hb=b88be742fac7a77a8095e8155ba8790db4b77568;hp=92a98eabe8c18288b80e52d5167971653f5c06b7;hpb=55cada301049cd3a4133ac8349d3f80db2a48b97;p=ffmpeg diff --git a/ffmpeg_vdpau.c b/ffmpeg_vdpau.c index 92a98eabe8c..cf2e83e8a3f 100644 --- a/ffmpeg_vdpau.c +++ b/ffmpeg_vdpau.c @@ -30,36 +30,31 @@ #include "libavutil/avassert.h" #include "libavutil/buffer.h" #include "libavutil/frame.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_vdpau.h" #include "libavutil/pixfmt.h" typedef struct VDPAUContext { - Display *dpy; - - VdpDevice device; - VdpDecoder decoder; - VdpGetProcAddress *get_proc_address; - - VdpGetErrorString *get_error_string; - VdpGetInformationString *get_information_string; - VdpDeviceDestroy *device_destroy; -#if 1 // for ffmpegs older vdpau API, not the oldest though - VdpDecoderCreate *decoder_create; - VdpDecoderDestroy *decoder_destroy; - VdpDecoderRender *decoder_render; -#endif - VdpVideoSurfaceCreate *video_surface_create; - VdpVideoSurfaceDestroy *video_surface_destroy; - VdpVideoSurfaceGetBitsYCbCr *video_surface_get_bits; - VdpVideoSurfaceGetParameters *video_surface_get_parameters; - VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities *video_surface_query; - + AVBufferRef *hw_frames_ctx; AVFrame *tmp_frame; - - enum AVPixelFormat pix_fmt; - VdpYCbCrFormat vdpau_format; } VDPAUContext; -int vdpau_api_ver = 2; +typedef struct VDPAUHWDevicePriv { + VdpDeviceDestroy *device_destroy; + Display *dpy; +} VDPAUHWDevicePriv; + +static void device_free(AVHWDeviceContext *ctx) +{ + AVVDPAUDeviceContext *hwctx = ctx->hwctx; + VDPAUHWDevicePriv *priv = ctx->user_opaque; + + if (priv->device_destroy) + priv->device_destroy(hwctx->device); + if (priv->dpy) + XCloseDisplay(priv->dpy); + av_freep(&priv); +} static void vdpau_uninit(AVCodecContext *s) { @@ -70,145 +65,72 @@ static void vdpau_uninit(AVCodecContext *s) ist->hwaccel_get_buffer = NULL; ist->hwaccel_retrieve_data = NULL; - if (ctx->decoder_destroy) - ctx->decoder_destroy(ctx->decoder); - - if (ctx->device_destroy) - ctx->device_destroy(ctx->device); - - if (ctx->dpy) - XCloseDisplay(ctx->dpy); - + av_buffer_unref(&ctx->hw_frames_ctx); av_frame_free(&ctx->tmp_frame); av_freep(&ist->hwaccel_ctx); av_freep(&s->hwaccel_context); } -static void vdpau_release_buffer(void *opaque, uint8_t *data) -{ - VdpVideoSurface surface = *(VdpVideoSurface*)data; - VDPAUContext *ctx = opaque; - - ctx->video_surface_destroy(surface); - av_freep(&data); -} - static int vdpau_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) { InputStream *ist = s->opaque; VDPAUContext *ctx = ist->hwaccel_ctx; - VdpVideoSurface *surface; - VdpStatus err; - VdpChromaType chroma; - uint32_t width, height; - - av_assert0(frame->format == AV_PIX_FMT_VDPAU); - - if (av_vdpau_get_surface_parameters(s, &chroma, &width, &height)) - return AVERROR(ENOSYS); - - surface = av_malloc(sizeof(*surface)); - if (!surface) - return AVERROR(ENOMEM); - - frame->buf[0] = av_buffer_create((uint8_t*)surface, sizeof(*surface), - vdpau_release_buffer, ctx, - AV_BUFFER_FLAG_READONLY); - if (!frame->buf[0]) { - av_freep(&surface); - return AVERROR(ENOMEM); - } - - // properly we should keep a pool of surfaces instead of creating - // them anew for each frame, but since we don't care about speed - // much in this code, we don't bother - err = ctx->video_surface_create(ctx->device, chroma, width, height, - surface); - if (err != VDP_STATUS_OK) { - av_log(NULL, AV_LOG_ERROR, "Error allocating a VDPAU video surface: %s\n", - ctx->get_error_string(err)); - av_buffer_unref(&frame->buf[0]); - return AVERROR_UNKNOWN; - } - - frame->data[3] = (uint8_t*)(uintptr_t)*surface; - return 0; + return av_hwframe_get_buffer(ctx->hw_frames_ctx, frame, 0); } static int vdpau_retrieve_data(AVCodecContext *s, AVFrame *frame) { - VdpVideoSurface surface = (VdpVideoSurface)(uintptr_t)frame->data[3]; InputStream *ist = s->opaque; VDPAUContext *ctx = ist->hwaccel_ctx; - VdpStatus err; - int ret, chroma_type; - - err = ctx->video_surface_get_parameters(surface, &chroma_type, - &ctx->tmp_frame->width, - &ctx->tmp_frame->height); - if (err != VDP_STATUS_OK) { - av_log(NULL, AV_LOG_ERROR, "Error getting surface parameters: %s\n", - ctx->get_error_string(err)); - return AVERROR_UNKNOWN; - } - ctx->tmp_frame->format = ctx->pix_fmt; + int ret; - ret = av_frame_get_buffer(ctx->tmp_frame, 32); + ret = av_hwframe_transfer_data(ctx->tmp_frame, frame, 0); if (ret < 0) return ret; - ctx->tmp_frame->width = frame->width; - ctx->tmp_frame->height = frame->height; - - err = ctx->video_surface_get_bits(surface, ctx->vdpau_format, - (void * const *)ctx->tmp_frame->data, - ctx->tmp_frame->linesize); - if (err != VDP_STATUS_OK) { - av_log(NULL, AV_LOG_ERROR, "Error retrieving frame data from VDPAU: %s\n", - ctx->get_error_string(err)); - ret = AVERROR_UNKNOWN; - goto fail; - } - - if (ctx->vdpau_format == VDP_YCBCR_FORMAT_YV12) - FFSWAP(uint8_t*, ctx->tmp_frame->data[1], ctx->tmp_frame->data[2]); - ret = av_frame_copy_props(ctx->tmp_frame, frame); - if (ret < 0) - goto fail; + if (ret < 0) { + av_frame_unref(ctx->tmp_frame); + return ret; + } av_frame_unref(frame); av_frame_move_ref(frame, ctx->tmp_frame); - return 0; -fail: - av_frame_unref(ctx->tmp_frame); - return ret; + return 0; } -static const int vdpau_formats[][2] = { - { VDP_YCBCR_FORMAT_YV12, AV_PIX_FMT_YUV420P }, - { VDP_YCBCR_FORMAT_NV12, AV_PIX_FMT_NV12 }, - { VDP_YCBCR_FORMAT_YUYV, AV_PIX_FMT_YUYV422 }, - { VDP_YCBCR_FORMAT_UYVY, AV_PIX_FMT_UYVY422 }, -}; - static int vdpau_alloc(AVCodecContext *s) { InputStream *ist = s->opaque; int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; - AVVDPAUContext *vdpau_ctx; VDPAUContext *ctx; const char *display, *vendor; VdpStatus err; - int i; + int ret; + + VdpDevice device; + VdpGetProcAddress *get_proc_address; + VdpGetInformationString *get_information_string; + + VDPAUHWDevicePriv *device_priv = NULL; + AVBufferRef *device_ref = NULL; + AVHWDeviceContext *device_ctx; + AVVDPAUDeviceContext *device_hwctx; + AVHWFramesContext *frames_ctx; ctx = av_mallocz(sizeof(*ctx)); if (!ctx) return AVERROR(ENOMEM); + device_priv = av_mallocz(sizeof(*device_priv)); + if (!device_priv) { + av_freep(&ctx); + goto fail; + } + ist->hwaccel_ctx = ctx; ist->hwaccel_uninit = vdpau_uninit; ist->hwaccel_get_buffer = vdpau_get_buffer; @@ -218,16 +140,16 @@ static int vdpau_alloc(AVCodecContext *s) if (!ctx->tmp_frame) goto fail; - ctx->dpy = XOpenDisplay(ist->hwaccel_device); - if (!ctx->dpy) { + device_priv->dpy = XOpenDisplay(ist->hwaccel_device); + if (!device_priv->dpy) { av_log(NULL, loglevel, "Cannot open the X11 display %s.\n", XDisplayName(ist->hwaccel_device)); goto fail; } - display = XDisplayString(ctx->dpy); + display = XDisplayString(device_priv->dpy); - err = vdp_device_create_x11(ctx->dpy, XDefaultScreen(ctx->dpy), &ctx->device, - &ctx->get_proc_address); + err = vdp_device_create_x11(device_priv->dpy, XDefaultScreen(device_priv->dpy), + &device, &get_proc_address); if (err != VDP_STATUS_OK) { av_log(NULL, loglevel, "VDPAU device creation on X11 display %s failed.\n", display); @@ -237,63 +159,52 @@ static int vdpau_alloc(AVCodecContext *s) #define GET_CALLBACK(id, result) \ do { \ void *tmp; \ - err = ctx->get_proc_address(ctx->device, id, &tmp); \ + err = get_proc_address(device, id, &tmp); \ if (err != VDP_STATUS_OK) { \ av_log(NULL, loglevel, "Error getting the " #id " callback.\n"); \ goto fail; \ } \ - ctx->result = tmp; \ + result = tmp; \ } while (0) - GET_CALLBACK(VDP_FUNC_ID_GET_ERROR_STRING, get_error_string); - GET_CALLBACK(VDP_FUNC_ID_GET_INFORMATION_STRING, get_information_string); - GET_CALLBACK(VDP_FUNC_ID_DEVICE_DESTROY, device_destroy); - if (vdpau_api_ver == 1) { - GET_CALLBACK(VDP_FUNC_ID_DECODER_CREATE, decoder_create); - GET_CALLBACK(VDP_FUNC_ID_DECODER_DESTROY, decoder_destroy); - GET_CALLBACK(VDP_FUNC_ID_DECODER_RENDER, decoder_render); - } - GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_CREATE, video_surface_create); - GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, video_surface_destroy); - GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, video_surface_get_bits); - GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS, video_surface_get_parameters); - GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_QUERY_GET_PUT_BITS_Y_CB_CR_CAPABILITIES, - video_surface_query); - - for (i = 0; i < FF_ARRAY_ELEMS(vdpau_formats); i++) { - VdpBool supported; - err = ctx->video_surface_query(ctx->device, VDP_CHROMA_TYPE_420, - vdpau_formats[i][0], &supported); - if (err != VDP_STATUS_OK) { - av_log(NULL, loglevel, - "Error querying VDPAU surface capabilities: %s\n", - ctx->get_error_string(err)); - goto fail; - } - if (supported) - break; - } - if (i == FF_ARRAY_ELEMS(vdpau_formats)) { - av_log(NULL, loglevel, - "No supported VDPAU format for retrieving the data.\n"); - return AVERROR(EINVAL); - } - ctx->vdpau_format = vdpau_formats[i][0]; - ctx->pix_fmt = vdpau_formats[i][1]; - - if (vdpau_api_ver == 1) { - vdpau_ctx = av_vdpau_alloc_context(); - if (!vdpau_ctx) - goto fail; - vdpau_ctx->render = ctx->decoder_render; - - s->hwaccel_context = vdpau_ctx; - } else - if (av_vdpau_bind_context(s, ctx->device, ctx->get_proc_address, - AV_HWACCEL_FLAG_IGNORE_LEVEL)) + GET_CALLBACK(VDP_FUNC_ID_GET_INFORMATION_STRING, get_information_string); + GET_CALLBACK(VDP_FUNC_ID_DEVICE_DESTROY, device_priv->device_destroy); + + device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VDPAU); + if (!device_ref) + goto fail; + device_ctx = (AVHWDeviceContext*)device_ref->data; + device_hwctx = device_ctx->hwctx; + device_ctx->user_opaque = device_priv; + device_ctx->free = device_free; + device_hwctx->device = device; + device_hwctx->get_proc_address = get_proc_address; + + device_priv = NULL; + + ret = av_hwdevice_ctx_init(device_ref); + if (ret < 0) goto fail; - ctx->get_information_string(&vendor); + ctx->hw_frames_ctx = av_hwframe_ctx_alloc(device_ref); + if (!ctx->hw_frames_ctx) + goto fail; + av_buffer_unref(&device_ref); + + frames_ctx = (AVHWFramesContext*)ctx->hw_frames_ctx->data; + frames_ctx->format = AV_PIX_FMT_VDPAU; + frames_ctx->sw_format = s->sw_pix_fmt; + frames_ctx->width = s->coded_width; + frames_ctx->height = s->coded_height; + + ret = av_hwframe_ctx_init(ctx->hw_frames_ctx); + if (ret < 0) + goto fail; + + if (av_vdpau_bind_context(s, device, get_proc_address, 0)) + goto fail; + + get_information_string(&vendor); av_log(NULL, AV_LOG_VERBOSE, "Using VDPAU -- %s -- on X11 display %s, " "to decode input stream #%d:%d.\n", vendor, display, ist->file_index, ist->st->index); @@ -303,60 +214,22 @@ do { fail: av_log(NULL, loglevel, "VDPAU init failed for stream #%d:%d.\n", ist->file_index, ist->st->index); + if (device_priv) { + if (device_priv->device_destroy) + device_priv->device_destroy(device); + if (device_priv->dpy) + XCloseDisplay(device_priv->dpy); + } + av_freep(&device_priv); + av_buffer_unref(&device_ref); vdpau_uninit(s); return AVERROR(EINVAL); } -static int vdpau_old_init(AVCodecContext *s) -{ - InputStream *ist = s->opaque; - int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; - AVVDPAUContext *vdpau_ctx; - VDPAUContext *ctx; - VdpStatus err; - int profile, ret; - - if (!ist->hwaccel_ctx) { - ret = vdpau_alloc(s); - if (ret < 0) - return ret; - } - ctx = ist->hwaccel_ctx; - vdpau_ctx = s->hwaccel_context; - - ret = av_vdpau_get_profile(s, &profile); - if (ret < 0) { - av_log(NULL, loglevel, "No known VDPAU decoder profile for this stream.\n"); - return AVERROR(EINVAL); - } - - if (ctx->decoder) - ctx->decoder_destroy(ctx->decoder); - - err = ctx->decoder_create(ctx->device, profile, - s->coded_width, s->coded_height, - 16, &ctx->decoder); - if (err != VDP_STATUS_OK) { - av_log(NULL, loglevel, "Error creating the VDPAU decoder: %s\n", - ctx->get_error_string(err)); - return AVERROR_UNKNOWN; - } - - vdpau_ctx->decoder = ctx->decoder; - - ist->hwaccel_get_buffer = vdpau_get_buffer; - ist->hwaccel_retrieve_data = vdpau_retrieve_data; - - return 0; -} - int vdpau_init(AVCodecContext *s) { InputStream *ist = s->opaque; - if (vdpau_api_ver == 1) - return vdpau_old_init(s); - if (!ist->hwaccel_ctx) { int ret = vdpau_alloc(s); if (ret < 0)