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