On Sun,  3 Nov 2013 23:27:50 +0100, Anton Khirnov <an...@khirnov.net>
wrote:
> ---
>  Changelog       |    1 +
>  Makefile        |    5 +-
>  avconv.c        |    3 +
>  avconv.h        |    5 +
>  avconv_opt.c    |    2 +
>  avconv_vdpau.c  |  339
>  +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  configure       |   12 +-
>  doc/avconv.texi |    3 +
>  8 files changed, 367 insertions(+), 3 deletions(-)
>  create mode 100644 avconv_vdpau.c
> 
> diff --git a/Changelog b/Changelog
> index 74ef59f..2e870ab 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -47,6 +47,7 @@ version 10:
>  - Live HDS muxer
>  - setsar/setdar filters now support variables in ratio expressions
>  - dar variable in the scale filter now returns the actual DAR (i.e. a *
>  sar)
> +- support for decoding through VDPAU in avconv (the -hwaccel option)

I don't mind/care either way, but this begs the question whether
decoding-only hwaccel should be hidden within libav, or exposed. Because
for decoding only, there is really no need to expose the hwaccel
ugliness^Wcomplexity to libav users.

Admittedly, if the sole goal is to test hwaccel, then it is best to expose
it.

>  
>  
>  version 9:
> diff --git a/Makefile b/Makefile
> index e116905..3b194c5 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -63,7 +63,10 @@ PROGS-$(CONFIG_AVSERVER) += avserver
>  
>  PROGS      := $(PROGS-yes:%=%$(EXESUF))
>  OBJS        = cmdutils.o $(EXEOBJS)
> +
>  OBJS-avconv = avconv_opt.o avconv_filter.o
> +OBJS-avconv-$(HAVE_VDPAU_X11) += avconv_vdpau.o
> +
>  TESTTOOLS   = audiogen videogen rotozoom tiny_psnr base64
>  HOSTPROGS  := $(TESTTOOLS:%=tests/%) doc/print_options
>  TOOLS       = qt-faststart trasher
> @@ -127,7 +130,7 @@ endef
>  $(foreach D,$(FFLIBS),$(eval $(call DOSUBDIR,lib$(D))))
>  
>  define DOPROG
> -OBJS-$(1) += $(1).o cmdutils.o $(EXEOBJS)
> +OBJS-$(1) += $(1).o cmdutils.o $(EXEOBJS) $(OBJS-$(1)-yes)
>  $(1)$(EXESUF): $$(OBJS-$(1))
>  $$(OBJS-$(1)): CFLAGS  += $(CFLAGS-$(1))
>  $(1)$(EXESUF): LDFLAGS += $(LDFLAGS-$(1))
> diff --git a/avconv.c b/avconv.c
> index 33a9a73..2ce52bb 100644
> --- a/avconv.c
> +++ b/avconv.c
> @@ -1375,6 +1375,9 @@ typedef struct HWAccel {
>  } HWAccel;
>  
>  static const HWAccel hwaccels[] = {
> +#if HAVE_VDPAU_X11
> +    { "VDPAU", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU },
> +#endif
>      { 0 },
>  };
>  
> diff --git a/avconv.h b/avconv.h
> index 232182a..6d0822d 100644
> --- a/avconv.h
> +++ b/avconv.h
> @@ -200,6 +200,7 @@ typedef struct FilterGraph {
>  enum HWAccelID {
>      HWACCEL_NONE = 0,
>      HWACCEL_AUTO,
> +    HWACCEL_VDPAU,
>  };
>  
>  typedef struct InputStream {
> @@ -386,4 +387,8 @@ FilterGraph *init_simple_filtergraph(InputStream
*ist,
> OutputStream *ost);
>  
>  int avconv_parse_options(int argc, char **argv);
>  
> +#if HAVE_VDPAU_X11
> +int vdpau_init(AVCodecContext *s);
> +#endif
> +
>  #endif /* AVCONV_H */
> diff --git a/avconv_opt.c b/avconv_opt.c
> index d3677be..19dd9d8 100644
> --- a/avconv_opt.c
> +++ b/avconv_opt.c
> @@ -494,6 +494,8 @@ static void add_input_streams(OptionsContext *o,
> AVFormatContext *ic)
>                      ist->hwaccel_id = HWACCEL_NONE;
>                  else if (!strcmp(hwaccel, "auto"))
>                      ist->hwaccel_id = HWACCEL_AUTO;
> +                else if (!strcmp(hwaccel, "vdpau"))
> +                    ist->hwaccel_id = HWACCEL_VDPAU;
>                  else {
>                      av_log(NULL, AV_LOG_ERROR, "Unrecognized hwaccel:
>                      %s.\n",
>                             hwaccel);
> diff --git a/avconv_vdpau.c b/avconv_vdpau.c
> new file mode 100644
> index 0000000..aeb97b1
> --- /dev/null
> +++ b/avconv_vdpau.c
> @@ -0,0 +1,339 @@
> +/*
> + * This file is part of Libav.
> + *
> + * Libav is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * Libav is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with Libav; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +#include <stdlib.h>
> +
> +#include <X11/Xlib.h>
> +
> +#include "avconv.h"
> +
> +#include "libavcodec/vdpau.h"
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/buffer.h"
> +#include "libavutil/frame.h"
> +#include "libavutil/pixfmt.h"
> +
> +typedef struct VDPAUContext {
> +    Display *dpy;
> +    int screen;
> +
> +    VdpDevice  device;
> +    VdpDecoder decoder;
> +    VdpGetProcAddress *get_proc_address;
> +
> +    VdpGetErrorString                               *get_error_string;
> +    VdpGetInformationString                        
> *get_information_string;
> +    VdpDeviceDestroy                                *device_destroy;
> +    VdpDecoderCreate                                *decoder_create;
> +    VdpDecoderDestroy                               *decoder_destroy;
> +    VdpDecoderRender                                *decoder_render;
> +    VdpVideoSurfaceCreate                          
*video_surface_create;
> +    VdpVideoSurfaceDestroy                         
> *video_surface_destroy;
> +    VdpVideoSurfaceGetBitsYCbCr                    
> *video_surface_get_bits;
> +    VdpVideoSurfaceGetParameters                   
> *video_surface_get_parameters;
> +    VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities
*video_surface_query;
> +
> +    AVFrame *tmp_frame;
> +
> +    enum AVPixelFormat pix_fmt;
> +    VdpYCbCrFormat vdpau_format;
> +} VDPAUContext;
> +
> +static void vdpau_uninit(AVCodecContext *s)
> +{
> +    InputStream  *ist = s->opaque;
> +    VDPAUContext *ctx = ist->hwaccel_ctx;
> +
> +    ist->hwaccel_uninit        = NULL;
> +    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);

These two stanzas look a bit fishy to me, since the function pointer is
initialized *before* the handle is created.

Furthermore, device_destroy cannot be NULL for a *valid* VDPAU back-end
(there is no other way to undo the device creation).

> +
> +    if (ctx->dpy)
> +        XCloseDisplay(ctx->dpy);
> +
> +    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)
> +{
> +    VdpVideoSurface *surface = NULL;
> +    InputStream         *ist = s->opaque;
> +    VDPAUContext        *ctx = ist->hwaccel_ctx;
> +    VdpStatus err;
> +
> +    av_assert0(frame->format == AV_PIX_FMT_VDPAU);
> +
> +    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);
> +    }
> +
> +    err = ctx->video_surface_create(ctx->device, VDP_CHROMA_TYPE_420,
> +                                    frame->width, frame->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*)*surface;
> +
> +    return 0;
> +}
> +
> +static int vdpau_retrieve_data(AVCodecContext *s, AVFrame *frame)
> +{
> +    VdpVideoSurface surface = (VdpVideoSurface)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;
> +
> +    ret = av_frame_get_buffer(ctx->tmp_frame, 32);
> +    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;
> +
> +    av_frame_unref(frame);
> +    av_frame_move_ref(frame, ctx->tmp_frame);
> +    return 0;
> +
> +fail:
> +    av_frame_unref(ctx->tmp_frame);
> +    return ret;
> +}
> +
> +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_VDPAU) ? AV_LOG_ERROR :
> AV_LOG_VERBOSE;
> +    AVVDPAUContext *vdpau_ctx;
> +    VDPAUContext *ctx;
> +    const char *display, *vendor;
> +    VdpStatus err;
> +    int i;
> +
> +    ctx = av_mallocz(sizeof(*ctx));
> +    if (!ctx)
> +        return AVERROR(ENOMEM);
> +
> +    ist->hwaccel_ctx           = ctx;
> +    ist->hwaccel_uninit        = vdpau_uninit;
> +    ist->hwaccel_get_buffer    = vdpau_get_buffer;
> +    ist->hwaccel_retrieve_data = vdpau_retrieve_data;
> +
> +    ctx->tmp_frame = av_frame_alloc();
> +    if (!ctx->tmp_frame)
> +        goto fail;
> +
> +    display = getenv("DISPLAY");
> +    if (!display) {
> +        av_log(NULL, loglevel, "DISPLAY not set.\n");
> +        goto fail;
> +    }
> +
> +    ctx->dpy = XOpenDisplay(display);
> +    if (!ctx->dpy) {
> +        av_log(NULL, loglevel, "Cannot open display %s.\n",
> +               display);
> +        goto fail;
> +    }
> +
> +    ctx->screen = XDefaultScreen(ctx->dpy);
> +
> +    err = vdp_device_create_x11(ctx->dpy, ctx->screen, &ctx->device,
> +                                &ctx->get_proc_address);
> +    if (err != VDP_STATUS_OK) {
> +        av_log(NULL, loglevel,
> +               "VDPAU device creation on display %s, screen %d
failed.\n",
> +               display, ctx->screen);
> +        goto fail;
> +    }
> +
> +#define GET_CALLBACK(id, result)                                       

>      \
> +do {                                                                   

>      \
> +    err = ctx->get_proc_address(ctx->device, id, (void**)&ctx->result);

>      \
> +    if (err != VDP_STATUS_OK) {                                        

>      \
> +        av_log(NULL, loglevel, "Error getting the " #id "
callback.\n"); 
>      \
> +        goto fail;                                                     

>      \
> +    }                                                                  

>      \
> +} 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);
> +    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];
> +
> +    vdpau_ctx = av_vdpau_alloc_context();
> +    if (!vdpau_ctx)
> +        goto fail;
> +    vdpau_ctx->render = ctx->decoder_render;
> +
> +    s->hwaccel_context = vdpau_ctx;
> +
> +    ctx->get_information_string(&vendor);
> +    av_log(NULL, AV_LOG_VERBOSE, "Using VDPAU -- %s -- on X display %s,
"
> +           "screen %d to decode input stream #%d:%d.\n", vendor,
> +           display, ctx->screen, ist->file_index, ist->st->index);
> +
> +    return 0;
> +
> +fail:
> +    av_log(NULL, loglevel, "VDPAU init failed for stream #%d:%d.\n",
> +           ist->file_index, ist->st->index);
> +    vdpau_uninit(s);
> +    return AVERROR(EINVAL);
> +}
> +
> +int vdpau_init(AVCodecContext *s)
> +{
> +    InputStream *ist = s->opaque;
> +    int loglevel = (ist->hwaccel_id == HWACCEL_VDPAU) ? AV_LOG_ERROR :
> AV_LOG_VERBOSE;
> +    AVVDPAUContext *vdpau_ctx;
> +    VDPAUContext *ctx;
> +    VdpStatus err;
> +    int profile, ret;
> +
> +    ist->hwaccel_get_buffer    = NULL;
> +    ist->hwaccel_retrieve_data = NULL;
> +
> +    if (!ist->hwaccel_ctx) {
> +        ret = vdpau_alloc(s);
> +        if (ret < 0)
> +            return ret;
> +    }
> +    ctx       = ist->hwaccel_ctx;
> +    vdpau_ctx = s->hwaccel_context;
> +
> +    profile = av_vdpau_get_profile(s);
> +    if (profile < 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;
> +}
> diff --git a/configure b/configure
> index 4aa66c0..e0b3ac8 100755
> --- a/configure
> +++ b/configure
> @@ -1367,11 +1367,13 @@ HAVE_LIST="
>      threads
>      unistd_h
>      usleep
> +    vdpau_x11
>      vfp_args
>      VirtualAlloc
>      windows_h
>      winsock2_h
>      xform_asm
> +    xlib
>      xmm_clobbers
>  "
>  
> @@ -3890,15 +3892,21 @@ if enabled libcdio; then
>      check_lib2 "cdio/paranoia/cdda.h cdio/paranoia/paranoia.h"
>      cdio_cddap_open -lcdio_paranoia -lcdio_cdda -lcdio
>  fi
>  
> +check_lib X11/Xlib.h XOpenDisplay -lX11 && enable xlib
> +
>  enabled x11grab                                           &&
> -require X11 X11/Xlib.h XOpenDisplay -lX11                 &&
>  require Xext X11/extensions/XShm.h XShmCreateImage -lXext &&
> -require Xfixes X11/extensions/Xfixes.h XFixesGetCursorImage -lXfixes
> +require Xfixes X11/extensions/Xfixes.h XFixesGetCursorImage -lXfixes &&
> +{ enabled xlib || die "ERROR: Xlib not found"; }
>  
>  enabled vdpau &&
>      check_cpp_condition vdpau/vdpau.h "defined
>      VDP_DECODER_PROFILE_MPEG4_PART2_ASP" ||
>      disable vdpau
>  
> +enabled vdpau && enabled xlib &&
> +check_lib2 "vdpau/vdpau.h vdpau/vdpau_x11.h" vdp_device_create_x11
> -lvdpau &&
> +enable vdpau_x11
> +
>  enabled debug && add_cflags -g"$debuglevel" && add_asflags
-g"$debuglevel"
>  
>  # add some useful compiler flags if supported
> diff --git a/doc/avconv.texi b/doc/avconv.texi
> index 3fb6277..e919828 100644
> --- a/doc/avconv.texi
> +++ b/doc/avconv.texi
> @@ -562,6 +562,9 @@ Do not use any hardware acceleration (the default).
>  
>  @item auto
>  Automatically select the hardware acceleration method.
> +
> +@item vdpau
> +Use VDPAU (Video Decode and Presentation API for Unix) hardware
> acceleration.
>  @end table
>  
>  This option has no effect if the selected hwaccel is not available or
not

-- 
Rémi Denis-Courmont
Sent from my collocated server
_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to