[FFmpeg-devel] [PATCH v3] vaapi_h264enc: Add Hardware Accelerated H.264 Encoder based on VAAPI
hamza at mayartech.com
hamza at mayartech.com
Tue Jan 26 11:23:43 CET 2016
From: "bryan.christ at mediafire.com" <bryan.christ at mediafire.com>
This commit adds a hardware accelerated H.264 encoder which utilizes
libva (open source implementation of VA-API). Information about libva
is available at: https://en.wikipedia.org/wiki/Video_Acceleration_API
This encoder is only availbale on linux and supported hardware which
can be viewed at:
https://en.wikipedia.org/wiki/Video_Acceleration_API#Supported_hardware_and_drivers
If libva is installed then the encoder will be automatically enabled.
---
This is the 3rd version of the patch. The subject of the first two patch
emails was:
libi264: Add Hardware Accelerated H.264 Encoder based on libVA
Bryan Christ <bryan.christ at mediafire> is reponsible for maintaining all the
files related to this patch. This patch is being submitted on his behalf.
Changes for v3:
- name of the encoder has been changed from libi264 to vaapi_h264enc to
make it more generic
- all the changes recommended by Michael Niedermayer, compn, James Almer,
Hendrik Leppkes, Carl Eugen Hoyos, Will Kelleher have been incorporated
Changelog | 1 +
MAINTAINERS | 1 +
configure | 8 +-
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/vaapi_display.c | 124 ++++
libavcodec/vaapi_display.h | 80 +++
libavcodec/vaapi_display_drm.c | 98 +++
libavcodec/vaapi_display_x11.c | 179 +++++
libavcodec/vaapi_h264enc.c | 1349 +++++++++++++++++++++++++++++++++++
libavcodec/vaapi_h264enc.h | 111 +++
libavcodec/vaapi_h264enc_paramset.c | 425 +++++++++++
libavcodec/vaapi_h264enc_paramset.h | 81 +++
libavcodec/version.h | 2 +-
14 files changed, 2459 insertions(+), 2 deletions(-)
create mode 100644 libavcodec/vaapi_display.c
create mode 100644 libavcodec/vaapi_display.h
create mode 100644 libavcodec/vaapi_display_drm.c
create mode 100644 libavcodec/vaapi_display_x11.c
create mode 100644 libavcodec/vaapi_h264enc.c
create mode 100644 libavcodec/vaapi_h264enc.h
create mode 100644 libavcodec/vaapi_h264enc_paramset.c
create mode 100644 libavcodec/vaapi_h264enc_paramset.h
diff --git a/Changelog b/Changelog
index d9c2ea8..1852910 100644
--- a/Changelog
+++ b/Changelog
@@ -49,6 +49,7 @@ version <next>:
- VAAPI VP9 hwaccel
- audio high-order multiband parametric equalizer
- automatic bitstream filtering
+- H.264 hwaccelerated encoding through VAAPI
version 2.8:
diff --git a/MAINTAINERS b/MAINTAINERS
index 9add13d..7cead7c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -203,6 +203,7 @@ Codecs:
libcelt_dec.c Nicolas George
libdirac* David Conrad
libgsm.c Michel Bardiaux
+ vaapi_h264enc*, vaapi_display* Bryan Christ
libkvazaar.c Arttu Ylä-Outinen
libopenjpeg.c Jaikrishnan Menon
libopenjpegenc.c Michael Bradshaw
diff --git a/configure b/configure
index da74ccd..cdd9a9b 100755
--- a/configure
+++ b/configure
@@ -265,6 +265,7 @@ External library support:
--enable-libwavpack enable wavpack encoding via libwavpack [no]
--enable-libwebp enable WebP encoding via libwebp [no]
--enable-libx264 enable H.264 encoding via x264 [no]
+# --enable-vaapienc-h264 enable H.264 encoding via VAAPI [autodetect]
--enable-libx265 enable HEVC encoding via x265 [no]
--enable-libxavs enable AVS encoding via xavs [no]
--enable-libxcb enable X11 grabbing using XCB [autodetect]
@@ -1972,6 +1973,7 @@ HAVE_LIST="
section_data_rel_ro
texi2html
threads
+ vaapi_drm
vaapi_x11
vdpau_x11
xlib
@@ -2658,7 +2660,6 @@ libwebp_anim_encoder_deps="libwebp"
libx262_encoder_deps="libx262"
libx264_encoder_deps="libx264"
libx264rgb_encoder_deps="libx264"
-libx264rgb_encoder_select="libx264_encoder"
libx265_encoder_deps="libx265"
libxavs_encoder_deps="libxavs"
libxvid_encoder_deps="libxvid"
@@ -2668,6 +2669,7 @@ libzvbi_teletext_decoder_deps="libzvbi"
nvenc_encoder_deps="nvenc"
nvenc_h264_encoder_deps="nvenc"
nvenc_hevc_encoder_deps="nvenc"
+vaapi_h264_encoder_deps="vaapi swscale"
# demuxers / muxers
ac3_demuxer_select="ac3_parser"
@@ -5734,6 +5736,10 @@ enabled vdpau && enabled xlib &&
prepend ffmpeg_libs $($ldflags_filter "-lvdpau") &&
enable vdpau_x11
+enabled vaapi &&
+ check_lib2 "va/va.h va/va_drm.h" vaGetDisplayDRM -lva -lva-drm &&
+ enable vaapi_drm
+
# Funny iconv installations are not unusual, so check it after all flags have been set
disabled iconv || check_func_headers iconv.h iconv || check_lib2 iconv.h iconv -liconv || disable iconv
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 0717d0a..2d2f0b5 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -847,6 +847,7 @@ OBJS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.o libwebpenc.o
OBJS-$(CONFIG_LIBWEBP_ANIM_ENCODER) += libwebpenc_common.o libwebpenc_animencoder.o
OBJS-$(CONFIG_LIBX262_ENCODER) += libx264.o
OBJS-$(CONFIG_LIBX264_ENCODER) += libx264.o
+OBJS-$(CONFIG_VAAPI_H264_ENCODER) += vaapi_h264enc.o vaapi_h264enc_paramset.o vaapi_display_x11.o vaapi_display_drm.o vaapi_display.o
OBJS-$(CONFIG_LIBX265_ENCODER) += libx265.o
OBJS-$(CONFIG_LIBXAVS_ENCODER) += libxavs.o
OBJS-$(CONFIG_LIBXVID_ENCODER) += libxvid.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 4eeb6f3..19ed4e5 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -592,6 +592,7 @@ void avcodec_register_all(void)
REGISTER_ENCODER(LIBXVID, libxvid);
REGISTER_DECODER(LIBZVBI_TELETEXT, libzvbi_teletext);
REGISTER_ENCODER(LIBAACPLUS, libaacplus);
+ REGISTER_ENCODER(VAAPI_H264, vaapi_h264);
/* text */
REGISTER_DECODER(BINTEXT, bintext);
diff --git a/libavcodec/vaapi_display.c b/libavcodec/vaapi_display.c
new file mode 100644
index 0000000..02bf3fb
--- /dev/null
+++ b/libavcodec/vaapi_display.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <va/va.h>
+
+#include "config.h"
+#include "libavutil/log.h"
+#include "vaapi_display.h"
+
+
+#if HAVE_VAAPI_X11
+extern const VADisplayHooks va_display_hooks_x11;
+#endif
+
+#if HAVE_VAAPI_DRM
+extern const VADisplayHooks va_display_hooks_drm;
+#endif
+
+static const VADisplayHooks *g_display_hooks;
+static const VADisplayHooks *g_display_hooks_available[] = {
+#if HAVE_VAAPI_DRM
+ &va_display_hooks_drm,
+#endif
+#if HAVE_VAAPI_X11
+ &va_display_hooks_x11,
+#endif
+ NULL
+};
+
+VADisplay
+ff_va_open_display(VADisplayType display)
+{
+ VADisplay va_dpy = NULL;
+ unsigned int i;
+
+ for (i = 0; !va_dpy && g_display_hooks_available[i]; i++) {
+ g_display_hooks = g_display_hooks_available[i];
+
+ if(g_display_hooks->display != display)
+ continue;
+
+ if (!g_display_hooks->open_display)
+ break;
+
+ va_dpy = g_display_hooks->open_display();
+ if (!va_dpy) {
+ av_log(NULL, AV_LOG_ERROR, "error: failed to open display --> %s\n", g_display_hooks->name);
+ }
+ break;
+ }
+
+ if (!va_dpy) {
+ av_log(NULL, AV_LOG_ERROR, "error: display device not found\n");
+ }
+
+ return va_dpy;
+}
+
+void
+ff_va_close_display(VADisplay va_dpy)
+{
+ if (!va_dpy)
+ return;
+
+ if (g_display_hooks && g_display_hooks->close_display)
+ g_display_hooks->close_display(va_dpy);
+}
+
+VAStatus
+ff_va_put_surface(
+ VADisplay va_dpy,
+ VASurfaceID surface,
+ const VARectangle *src_rect,
+ const VARectangle *dst_rect
+)
+{
+ if (!va_dpy)
+ return VA_STATUS_ERROR_INVALID_DISPLAY;
+
+ if (g_display_hooks && g_display_hooks->put_surface)
+ return g_display_hooks->put_surface(va_dpy, surface, src_rect, dst_rect);
+ return VA_STATUS_ERROR_UNIMPLEMENTED;
+}
diff --git a/libavcodec/vaapi_display.h b/libavcodec/vaapi_display.h
new file mode 100644
index 0000000..3d2c81c
--- /dev/null
+++ b/libavcodec/vaapi_display.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VAAPI_DISPLAY_H
+#define AVCODEC_VAAPI_DISPLAY_H
+
+#include <va/va.h>
+
+
+typedef enum VADisplayType
+{
+ VA_DISPLAY_DRM = 0,
+ VA_DISPLAY_X11 = 1
+} VADisplayType;
+
+
+typedef struct VADisplayHooks
+{
+ const char *name;
+ VADisplayType display;
+
+ VADisplay (*open_display) (void);
+ void (*close_display) (VADisplay va_dpy);
+ VAStatus (*put_surface) (VADisplay va_dpy, VASurfaceID surface,
+ const VARectangle *src_rect,
+ const VARectangle *dst_rect);
+} VADisplayHooks;
+
+VADisplay
+ff_va_open_display(VADisplayType display);
+
+void
+ff_va_close_display(VADisplay va_dpy);
+
+VAStatus
+ff_va_put_surface(
+ VADisplay va_dpy,
+ VASurfaceID surface,
+ const VARectangle *src_rect,
+ const VARectangle *dst_rect
+);
+
+#endif /* AVCODEC_VAAPI_DISPLAY_H */
diff --git a/libavcodec/vaapi_display_drm.c b/libavcodec/vaapi_display_drm.c
new file mode 100644
index 0000000..bee5707
--- /dev/null
+++ b/libavcodec/vaapi_display_drm.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#if HAVE_VAAPI_DRM
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+# include <va/va_drm.h>
+
+#include "vaapi_display.h"
+#include "libavutil/log.h"
+
+static int drm_fd = -1;
+
+static VADisplay
+va_open_display_drm(void)
+{
+ drm_fd = open("/dev/dri/card0", O_RDWR);
+ if (drm_fd < 0) {
+ av_log(NULL, AV_LOG_ERROR, "error: can't open DRM connection!\n");
+ return NULL;
+ }
+ return vaGetDisplayDRM(drm_fd);
+}
+
+static void
+va_close_display_drm(VADisplay va_dpy)
+{
+ if (drm_fd < 0)
+ return;
+
+ close(drm_fd);
+ drm_fd = -1;
+}
+
+
+static VAStatus
+va_put_surface_drm(
+ VADisplay va_dpy,
+ VASurfaceID surface,
+ const VARectangle *src_rect,
+ const VARectangle *dst_rect
+)
+{
+ return VA_STATUS_ERROR_OPERATION_FAILED;
+}
+
+const VADisplayHooks va_display_hooks_drm = {
+ "drm",
+ VA_DISPLAY_DRM,
+ va_open_display_drm,
+ va_close_display_drm,
+ va_put_surface_drm,
+};
+
+#endif /* HAVE_VAAPI_DRM */
diff --git a/libavcodec/vaapi_display_x11.c b/libavcodec/vaapi_display_x11.c
new file mode 100644
index 0000000..02497a9
--- /dev/null
+++ b/libavcodec/vaapi_display_x11.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#if HAVE_VAAPI_X11
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <va/va_x11.h>
+
+#include "vaapi_display.h"
+#include "libavutil/log.h"
+
+
+static Display *x11_display;
+static Window x11_window;
+
+
+static VADisplay
+va_open_display_x11(void)
+{
+ Display *local_display = NULL;
+ int i = 0;
+ const char *display_names[] = { ":0", ":1", NULL };
+
+ do {
+ local_display = XOpenDisplay(display_names[i++]);
+
+ if(local_display != NULL) // found a display
+ break;
+ } while(display_names[i] != NULL);
+
+ if(local_display == NULL) {
+ av_log(NULL, AV_LOG_ERROR, "error: can't connect to X server!\n");
+ return NULL;
+ }
+
+ x11_display = local_display;
+
+ return vaGetDisplay(x11_display);
+}
+
+static void
+va_close_display_x11(VADisplay va_dpy)
+{
+ if (!x11_display)
+ return;
+
+ if (x11_window) {
+ XUnmapWindow(x11_display, x11_window);
+ XDestroyWindow(x11_display, x11_window);
+ x11_window = None;
+ }
+ XCloseDisplay(x11_display);
+ x11_display = NULL;
+}
+
+static int
+ensure_window(unsigned int width, unsigned int height)
+{
+ Window win, rootwin;
+ unsigned int black_pixel, white_pixel;
+ int screen;
+
+ if (!x11_display)
+ return 0;
+
+ if (x11_window) {
+ XResizeWindow(x11_display, x11_window, width, height);
+ return 1;
+ }
+
+ screen = DefaultScreen(x11_display);
+ rootwin = RootWindow(x11_display, screen);
+ black_pixel = BlackPixel(x11_display, screen);
+ white_pixel = WhitePixel(x11_display, screen);
+
+ win = XCreateSimpleWindow(
+ x11_display,
+ rootwin,
+ 0, 0, width, height,
+ 1, black_pixel, white_pixel
+ );
+ if (!win)
+ return 0;
+ x11_window = win;
+
+ XMapWindow(x11_display, x11_window);
+ XSync(x11_display, False);
+ return 1;
+}
+
+static inline bool
+validate_rect(const VARectangle *rect)
+{
+ return (rect &&
+ rect->x >= 0 &&
+ rect->y >= 0 &&
+ rect->width > 0 &&
+ rect->height > 0);
+}
+
+static VAStatus
+va_put_surface_x11(
+ VADisplay va_dpy,
+ VASurfaceID surface,
+ const VARectangle *src_rect,
+ const VARectangle *dst_rect
+)
+{
+ unsigned int win_width, win_height;
+
+ if (!va_dpy)
+ return VA_STATUS_ERROR_INVALID_DISPLAY;
+ if (surface == VA_INVALID_SURFACE)
+ return VA_STATUS_ERROR_INVALID_SURFACE;
+ if (!validate_rect(src_rect) || !validate_rect(dst_rect))
+ return VA_STATUS_ERROR_INVALID_PARAMETER;
+
+ win_width = dst_rect->x + dst_rect->width;
+ win_height = dst_rect->y + dst_rect->height;
+ if (!ensure_window(win_width, win_height))
+ return VA_STATUS_ERROR_ALLOCATION_FAILED;
+ return vaPutSurface(va_dpy, surface, x11_window,
+ src_rect->x, src_rect->y,
+ src_rect->width, src_rect->height,
+ dst_rect->x, dst_rect->y,
+ dst_rect->width, dst_rect->height,
+ NULL, 0,
+ VA_FRAME_PICTURE);
+}
+
+const VADisplayHooks va_display_hooks_x11 = {
+ "x11",
+ VA_DISPLAY_X11,
+ va_open_display_x11,
+ va_close_display_x11,
+ va_put_surface_x11,
+};
+
+#endif /* HAVE_VAAPI_X11 */
diff --git a/libavcodec/vaapi_h264enc.c b/libavcodec/vaapi_h264enc.c
new file mode 100644
index 0000000..62bf918
--- /dev/null
+++ b/libavcodec/vaapi_h264enc.c
@@ -0,0 +1,1349 @@
+/*
+ * Interface for VAAPI H.264 encoding using libva library meant for hardware
+ * encoding on intel processors
+ * Copyright (C) 2015 Bryan Christ
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "vaapi_h264enc.h"
+#include "vaapi_h264enc_paramset.h"
+#include "avcodec.h"
+#include "libavutil/internal.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/opt.h"
+#include "libavcodec/internal.h"
+#include "libswscale/swscale.h"
+
+
+#define FRAME_P 0
+#define FRAME_B 1
+#define FRAME_I 2
+#define FRAME_IDR 7
+
+
+#define MAX_FRAME_NUM (2<<16)
+#define MAX_PIC_ORDER_CNT_LSB (2<<8)
+#define LOG2_MAX_FRAME_NUM 16
+#define LOG2_MAX_PIC_ORDER_CNT_LSB 8
+
+
+#define CHECK_VASTATUS(avctx, va_status,func) \
+ if (va_status != VA_STATUS_SUCCESS) { \
+ av_log(avctx, AV_LOG_ERROR, "%s:%s (%d) failed\n", __func__, func, __LINE__); \
+ return -1; \
+ }
+
+#define current_slot(ictx) (ictx->current_frame_display % SURFACE_NUM)
+
+
+static void vaapi_h264enc_param_default(AVCodecContext *avctx, VaapiH264EncContext *ctx)
+{
+
+ if(avctx->gop_size > 0)
+ ctx->intra_idr_period = avctx->gop_size;
+ else
+ ctx->intra_idr_period = 250;
+
+ if(ctx->intra_period <= 0 ||
+ ctx->intra_period > ctx->intra_idr_period ||
+ ctx->intra_idr_period % ctx->intra_period != 0)
+ ctx->intra_period = ctx->intra_idr_period;
+
+ if(!ctx->profile)
+ ctx->profile = VAProfileH264High;
+
+ if(ctx->rc_mode < 0)
+ ctx->rc_mode = VA_RC_VBR;
+
+ if(avctx->bit_rate > 0) {
+ ctx->rc_mode = VA_RC_CBR;
+ ctx->frame_bitrate = avctx->bit_rate;
+ } else {
+ ctx->frame_bitrate = 0;
+ }
+
+ if(avctx->max_b_frames >= 0)
+ ctx->ip_period = 1 + avctx->max_b_frames;
+ else
+ ctx->ip_period = 1;
+
+ if(ctx->ip_period >= ctx->intra_period)
+ ctx->ip_period = ctx->intra_period - 1;
+
+ ctx->constraint_set_flag = 0;
+ ctx->config_attrib_num = 0;
+ ctx->h264_packedheader = 0;
+ ctx->h264_maxref = (1<<16|1);
+ ctx->num_ref_frames = 2;
+
+ if(avctx->qmin >= 0)
+ ctx->initial_qp = avctx->qmin;
+ else
+ ctx->initial_qp = 26;
+
+ ctx->minimal_qp = 0;
+
+ ctx->current_frame_type = FRAME_IDR;
+ ctx->current_frame_display = 0;
+ ctx->current_frame_num = 0;
+ ctx->current_frame_encoding = 0;
+
+ ctx->nb_surfaces_loaded = 0;
+ ctx->last_p = 0;
+}
+
+static int
+build_packed_pic_buffer(VaapiH264EncContext *ictx, unsigned char **header_buffer)
+{
+ VaapiH264EncBitstream bs;
+
+ ff_vaapi_h264enc_bs_start(&bs);
+ ff_vaapi_h264enc_nal_start_code_prefix(&bs);
+ ff_vaapi_h264enc_nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS);
+ ff_vaapi_h264enc_pps_rbsp(&ictx->pic_param, &bs);
+ ff_vaapi_h264enc_bs_end(&bs);
+
+ *header_buffer = (unsigned char *)bs.buffer;
+ return bs.bit_offset;
+}
+
+static int
+build_packed_seq_buffer(VaapiH264EncContext *ictx, unsigned char **header_buffer)
+{
+ VaapiH264EncBitstream bs;
+
+ ff_vaapi_h264enc_bs_start(&bs);
+ ff_vaapi_h264enc_nal_start_code_prefix(&bs);
+ ff_vaapi_h264enc_nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS);
+ ff_vaapi_h264enc_sps_rbsp(ictx, &ictx->seq_param, &bs);
+ ff_vaapi_h264enc_bs_end(&bs);
+
+ *header_buffer = (unsigned char *)bs.buffer;
+ return bs.bit_offset;
+}
+
+static int
+build_packed_slice_buffer(VaapiH264EncContext *ictx, unsigned char **header_buffer)
+{
+ VaapiH264EncBitstream bs;
+ int is_idr = !!ictx->pic_param.pic_fields.bits.idr_pic_flag;
+ int is_ref = !!ictx->pic_param.pic_fields.bits.reference_pic_flag;
+
+ ff_vaapi_h264enc_bs_start(&bs);
+ ff_vaapi_h264enc_nal_start_code_prefix(&bs);
+
+ if (IS_I_SLICE(ictx->slice_param.slice_type)) {
+ ff_vaapi_h264enc_nal_header(&bs, NAL_REF_IDC_HIGH, is_idr ? NAL_IDR : NAL_NON_IDR);
+ } else if (IS_P_SLICE(ictx->slice_param.slice_type)) {
+ ff_vaapi_h264enc_nal_header(&bs, NAL_REF_IDC_MEDIUM, NAL_NON_IDR);
+ } else if (IS_B_SLICE(ictx->slice_param.slice_type)) {
+ ff_vaapi_h264enc_nal_header(&bs, is_ref ? NAL_REF_IDC_LOW : NAL_REF_IDC_NONE, NAL_NON_IDR);
+ } else {
+ return -1;
+ }
+
+ ff_vaapi_h264enc_slice_header(ictx, &bs);
+ ff_vaapi_h264enc_bs_end(&bs);
+
+ *header_buffer = (unsigned char *)bs.buffer;
+ return bs.bit_offset;
+}
+
+static int init_sps(AVCodecContext *avctx, VaapiH264EncContext *ctx, VAEncSequenceParameterBufferH264 *seq_param)
+{
+ seq_param->level_idc = 41 /*SH_LEVEL_3*/;
+ seq_param->picture_width_in_mbs = ctx->frame_width_mbaligned / 16;
+ seq_param->picture_height_in_mbs = ctx->frame_height_mbaligned / 16;
+ seq_param->bits_per_second = ctx->frame_bitrate;
+
+ seq_param->intra_period = ctx->intra_period;
+ seq_param->intra_idr_period = ctx->intra_idr_period;
+ seq_param->ip_period = ctx->ip_period;
+
+ seq_param->max_num_ref_frames = ctx->num_ref_frames;
+ seq_param->seq_fields.bits.frame_mbs_only_flag = 1;
+ seq_param->time_scale = 900;
+ seq_param->num_units_in_tick = 15; /* Tc = num_units_in_tick / time_sacle */
+ seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = LOG2_MAX_PIC_ORDER_CNT_LSB - 4;
+ seq_param->seq_fields.bits.log2_max_frame_num_minus4 = LOG2_MAX_FRAME_NUM - 4;
+ seq_param->seq_fields.bits.frame_mbs_only_flag = 1;
+ seq_param->seq_fields.bits.chroma_format_idc = 1;
+ seq_param->seq_fields.bits.direct_8x8_inference_flag = 1;
+
+ if (avctx->width != ctx->frame_width_mbaligned ||
+ avctx->height != ctx->frame_height_mbaligned) {
+ seq_param->frame_cropping_flag = 1;
+ seq_param->frame_crop_left_offset = 0;
+ seq_param->frame_crop_right_offset = (ctx->frame_width_mbaligned - avctx->width)/2;
+ seq_param->frame_crop_top_offset = 0;
+ seq_param->frame_crop_bottom_offset = (ctx->frame_height_mbaligned - avctx->height)/2;
+ }
+
+ return 0;
+}
+
+static int calc_poc(VaapiH264EncContext *ictx, int pic_order_cnt_lsb)
+{
+ static int pic_order_cnt_msb_ref = 0, pic_order_cnt_lsb_ref = 0;
+ int prev_pic_order_cnt_msb, prev_pic_order_cnt_lsb;
+ int pic_order_cnt_msb, top_field_order_cnt;
+
+ if (ictx->current_frame_type == FRAME_IDR)
+ prev_pic_order_cnt_msb = prev_pic_order_cnt_lsb = 0;
+ else {
+ prev_pic_order_cnt_msb = pic_order_cnt_msb_ref;
+ prev_pic_order_cnt_lsb = pic_order_cnt_lsb_ref;
+ }
+
+ if ((pic_order_cnt_lsb < prev_pic_order_cnt_lsb) &&
+ ((prev_pic_order_cnt_lsb - pic_order_cnt_lsb) >= (int)(MAX_PIC_ORDER_CNT_LSB / 2)))
+ pic_order_cnt_msb = prev_pic_order_cnt_msb + MAX_PIC_ORDER_CNT_LSB;
+ else if ((pic_order_cnt_lsb > prev_pic_order_cnt_lsb) &&
+ ((pic_order_cnt_lsb - prev_pic_order_cnt_lsb) > (int)(MAX_PIC_ORDER_CNT_LSB / 2)))
+ pic_order_cnt_msb = prev_pic_order_cnt_msb - MAX_PIC_ORDER_CNT_LSB;
+ else
+ pic_order_cnt_msb = prev_pic_order_cnt_msb;
+
+ top_field_order_cnt = pic_order_cnt_msb + pic_order_cnt_lsb;
+
+ if (ictx->current_frame_type != FRAME_B) {
+ pic_order_cnt_msb_ref = pic_order_cnt_msb;
+ pic_order_cnt_lsb_ref = pic_order_cnt_lsb;
+ }
+
+ return top_field_order_cnt;
+}
+
+#define partition(ref, field, key, ascending) \
+ while (i <= j) { \
+ if (ascending) { \
+ while (ref[i].field < key) \
+ i++; \
+ while (ref[j].field > key) \
+ j--; \
+ } else { \
+ while (ref[i].field > key) \
+ i++; \
+ while (ref[j].field < key) \
+ j--; \
+ } \
+ if (i <= j) { \
+ tmp = ref[i]; \
+ ref[i] = ref[j]; \
+ ref[j] = tmp; \
+ i++; \
+ j--; \
+ } \
+ } \
+
+static void sort_one(VAPictureH264 ref[], int left, int right,
+ int ascending, int frame_idx)
+{
+ int i = left, j = right;
+ unsigned int key;
+ VAPictureH264 tmp;
+
+ if (frame_idx) {
+ key = ref[(left + right) / 2].frame_idx;
+ partition(ref, frame_idx, key, ascending);
+ } else {
+ key = ref[(left + right) / 2].TopFieldOrderCnt;
+ partition(ref, TopFieldOrderCnt, (signed int)key, ascending);
+ }
+
+ /* recursion */
+ if (left < j)
+ sort_one(ref, left, j, ascending, frame_idx);
+
+ if (i < right)
+ sort_one(ref, i, right, ascending, frame_idx);
+}
+
+static void sort_two(VAPictureH264 ref[], int left, int right, unsigned int key, unsigned int frame_idx,
+ int partition_ascending, int list0_ascending, int list1_ascending)
+{
+ int i = left, j = right;
+ VAPictureH264 tmp;
+
+ if (frame_idx) {
+ partition(ref, frame_idx, key, partition_ascending);
+ } else {
+ partition(ref, TopFieldOrderCnt, (signed int)key, partition_ascending);
+ }
+
+ sort_one(ref, left, i-1, list0_ascending, frame_idx);
+ sort_one(ref, j+1, right, list1_ascending, frame_idx);
+}
+
+static int update_ref_pic_list(VaapiH264EncContext *ictx)
+{
+ unsigned int current_poc = ictx->current_curr_pic.TopFieldOrderCnt;
+
+ if (ictx->current_frame_type == FRAME_P) {
+ memcpy(ictx->ref_pic_list0_P, ictx->reference_frames, ictx->num_short_term * sizeof(VAPictureH264));
+ sort_one(ictx->ref_pic_list0_P, 0, ictx->num_short_term-1, 0, 1);
+ }
+
+ if (ictx->current_frame_type == FRAME_B) {
+ memcpy(ictx->ref_pic_list0_B, ictx->reference_frames, ictx->num_short_term * sizeof(VAPictureH264));
+ sort_two(ictx->ref_pic_list0_B, 0, ictx->num_short_term-1, current_poc, 0,
+ 1, 0, 1);
+
+ memcpy(ictx->ref_pic_list1_B, ictx->reference_frames,ictx-> num_short_term * sizeof(VAPictureH264));
+ sort_two(ictx->ref_pic_list1_B, 0, ictx->num_short_term-1, current_poc, 0,
+ 0, 1, 0);
+ }
+
+ return 0;
+}
+
+static void init_sei(VaapiH264EncContext *ictx)
+{
+ int init_cpb_size;
+ int target_bit_rate;
+
+ /* it comes for the bps defined in SPS */
+ target_bit_rate = ictx->seq_param.bits_per_second;
+ init_cpb_size = (target_bit_rate * 8) >> 10;
+ ictx->initial_cpb_removal_delay = init_cpb_size * 0.5 * 1024 / target_bit_rate * 90000;
+
+ ictx->cpb_removal_delay = 2;
+ ictx->initial_cpb_removal_delay_length = 24;
+ ictx->cpb_removal_delay_length = 24;
+ ictx->dpb_output_delay_length = 24;
+}
+
+static int init_pps(VaapiH264EncContext *ictx)
+{
+ int i = 0;
+
+ ictx->pic_param.CurrPic.picture_id = ictx->ref_surface[current_slot(ictx)];
+ ictx->pic_param.CurrPic.frame_idx = ictx->current_frame_num;
+ ictx->pic_param.CurrPic.flags = 0;
+ ictx->pic_param.CurrPic.TopFieldOrderCnt = calc_poc(ictx, (ictx->current_frame_display - ictx->current_IDR_display) % MAX_PIC_ORDER_CNT_LSB);
+ ictx->pic_param.CurrPic.BottomFieldOrderCnt = ictx->pic_param.CurrPic.TopFieldOrderCnt;
+ ictx->current_curr_pic = ictx->pic_param.CurrPic;
+
+ memcpy(ictx->pic_param.ReferenceFrames, ictx->reference_frames, ictx->num_short_term*sizeof(VAPictureH264));
+ for (i = ictx->num_short_term; i < SURFACE_NUM; i++) {
+ ictx->pic_param.ReferenceFrames[i].picture_id = VA_INVALID_SURFACE;
+ ictx->pic_param.ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID;
+ }
+
+ ictx->pic_param.pic_fields.bits.idr_pic_flag = (ictx->current_frame_type == FRAME_IDR);
+ ictx->pic_param.pic_fields.bits.reference_pic_flag = (ictx->current_frame_type != FRAME_B);
+ ictx->pic_param.pic_fields.bits.entropy_coding_mode_flag = ictx->entropy_coder;
+ ictx->pic_param.pic_fields.bits.deblocking_filter_control_present_flag = 0;
+ ictx->pic_param.frame_num = ictx->current_frame_num;
+ ictx->pic_param.coded_buf = ictx->coded_buf[current_slot(ictx)];
+ ictx->pic_param.last_picture = 0;
+ ictx->pic_param.pic_init_qp = ictx->initial_qp;
+
+ return 0;
+}
+
+static int render_sequence(AVCodecContext *avctx, VaapiH264EncContext *ictx)
+{
+ VABufferID seq_param_buf, hrd_buf_id, render_id[3];
+ VAStatus va_status;
+ VAEncMiscParameterHRD *misc_hrd_param;
+ VAEncMiscParameterBuffer *misc_param;
+
+ va_status = vaCreateBuffer(ictx->va_dpy, ictx->context_id,
+ VAEncSequenceParameterBufferType,
+ sizeof(ictx->seq_param),1,&ictx->seq_param,&seq_param_buf);
+
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+
+ /* hrd parameter */
+ vaCreateBuffer(ictx->va_dpy, ictx->context_id,
+ VAEncMiscParameterBufferType,
+ sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterRateControl),
+ 1, NULL, &hrd_buf_id);
+ CHECK_VASTATUS(avctx, va_status, "vaCreateBuffer");
+
+ vaMapBuffer(ictx->va_dpy, hrd_buf_id, (void **)&misc_param);
+ misc_param->type = VAEncMiscParameterTypeHRD;
+ misc_hrd_param = (VAEncMiscParameterHRD *)misc_param->data;
+
+ if (ictx->frame_bitrate > 0) {
+ misc_hrd_param->initial_buffer_fullness = ictx->frame_bitrate * 4;
+ misc_hrd_param->buffer_size = ictx->frame_bitrate * 8;
+ } else {
+ misc_hrd_param->initial_buffer_fullness = 0;
+ misc_hrd_param->buffer_size = 0;
+ }
+
+ render_id[0] = seq_param_buf;
+ //render_id[1] = rc_param_buf;
+ render_id[1] = hrd_buf_id;
+
+ va_status = vaRenderPicture(ictx->va_dpy, ictx->context_id, &render_id[0], 2);
+
+ CHECK_VASTATUS(avctx, va_status,"vaRenderPicture");
+
+ return 0;
+}
+
+static int render_picture(AVCodecContext *avctx, VaapiH264EncContext *ictx)
+{
+ VABufferID pic_param_buf;
+ VAStatus va_status;
+
+ init_pps(ictx);
+
+ va_status = vaCreateBuffer(ictx->va_dpy, ictx->context_id,VAEncPictureParameterBufferType,
+ sizeof(ictx->pic_param),1,&ictx->pic_param, &pic_param_buf);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+
+ va_status = vaRenderPicture(ictx->va_dpy, ictx->context_id, &pic_param_buf, 1);
+ CHECK_VASTATUS(avctx, va_status,"vaRenderPicture");
+
+ return 0;
+}
+
+static int init_va(AVCodecContext *avctx, VaapiH264EncContext *ictx)
+{
+ VAProfile profile_list[] = {VAProfileH264High, VAProfileH264Main, VAProfileH264ConstrainedBaseline};
+ VAEntrypoint *entrypoints;
+ int num_entrypoints, slice_entrypoint;
+ int support_encode = 0;
+ int major_ver, minor_ver;
+ VAStatus va_status;
+ unsigned int i;
+
+ ictx->va_dpy = ff_va_open_display(ictx->display);
+ if(!ictx->va_dpy) {
+ av_log(avctx, AV_LOG_ERROR, "error: Unable to open display\n");
+ return -1;
+ }
+
+ va_status = vaInitialize(ictx->va_dpy, &major_ver, &minor_ver);
+ CHECK_VASTATUS(avctx, va_status, "vaInitialize");
+
+ num_entrypoints = vaMaxNumEntrypoints(ictx->va_dpy);
+ entrypoints = malloc(num_entrypoints * sizeof(*entrypoints));
+ if (!entrypoints) {
+ av_log(avctx, AV_LOG_ERROR, "error: failed to initialize VA entrypoints array\n");
+ return -1;
+ }
+
+ /* use the highest profile */
+ for (i = 0; i < sizeof(profile_list)/sizeof(profile_list[0]); i++) {
+ if (ictx->profile != profile_list[i])
+ continue;
+
+ vaQueryConfigEntrypoints(ictx->va_dpy, ictx->profile, entrypoints, &num_entrypoints);
+ for (slice_entrypoint = 0; slice_entrypoint < num_entrypoints; slice_entrypoint++) {
+ if (entrypoints[slice_entrypoint] == VAEntrypointEncSlice) {
+ support_encode = 1;
+ break;
+ }
+ }
+ if (support_encode == 1)
+ break;
+ }
+
+ if (support_encode == 0) {
+ av_log(avctx, AV_LOG_ERROR, "Can't find VAEntrypointEncSlice for H264 profiles\n");
+ return -1;
+ } else {
+ switch (ictx->profile) {
+ case VAProfileH264Baseline:
+ av_log(avctx, AV_LOG_INFO, "Use profile VAProfileH264Baseline\n");
+ ictx->ip_period = 1;
+ ictx->constraint_set_flag |= (1 << 0); /* Annex A.2.1 */
+ ictx->entropy_coder = H264_CODER_CAVLC;
+ break;
+ case VAProfileH264ConstrainedBaseline:
+ av_log(avctx, AV_LOG_INFO, "Use profile VAProfileH264ConstrainedBaseline\n");
+ ictx->constraint_set_flag |= (1 << 0 | 1 << 1); /* Annex A.2.2 */
+ ictx->ip_period = 1;
+ ictx->entropy_coder = H264_CODER_CAVLC;
+ break;
+
+ case VAProfileH264Main:
+ av_log(avctx, AV_LOG_INFO, "Use profile VAProfileH264Main\n");
+ ictx->constraint_set_flag |= (1 << 1); /* Annex A.2.2 */
+ break;
+
+ case VAProfileH264High:
+ av_log(avctx, AV_LOG_INFO, "Use profile VAProfileH264High\n");
+ ictx->constraint_set_flag |= (1 << 3); /* Annex A.2.4 */
+ break;
+ default:
+ av_log(avctx, AV_LOG_INFO, "Unknown profile. Set to High");
+ ictx->constraint_set_flag |= (1 << 3); /* Annex A.2.4 */
+ break;
+ }
+ }
+
+ /* find out the format for the render target, and rate control mode */
+ for (i = 0; i < VAConfigAttribTypeMax; i++)
+ ictx->attrib[i].type = i;
+
+ va_status = vaGetConfigAttributes(ictx->va_dpy, ictx->profile, VAEntrypointEncSlice,
+ &ictx->attrib[0], VAConfigAttribTypeMax);
+ CHECK_VASTATUS(avctx, va_status, "vaGetConfigAttributes");
+ /* check the interested configattrib */
+ if ((ictx->attrib[VAConfigAttribRTFormat].value & VA_RT_FORMAT_YUV420) == 0) {
+ av_log(avctx, AV_LOG_ERROR, "Not find desired YUV420 RT format\n");
+ return -1;
+ } else {
+ ictx->config_attrib[ictx->config_attrib_num].type = VAConfigAttribRTFormat;
+ ictx->config_attrib[ictx->config_attrib_num].value = VA_RT_FORMAT_YUV420;
+ ictx->config_attrib_num++;
+ }
+
+ if (ictx->attrib[VAConfigAttribRateControl].value != VA_ATTRIB_NOT_SUPPORTED) {
+ int tmp = ictx->attrib[VAConfigAttribRateControl].value;
+
+ av_log(avctx, AV_LOG_INFO, "Support rate control mode (0x%x):", tmp);
+
+ if (tmp & VA_RC_NONE)
+ av_log(avctx, AV_LOG_INFO, "NONE ");
+ if (tmp & VA_RC_CBR)
+ av_log(avctx, AV_LOG_INFO, "CBR ");
+ if (tmp & VA_RC_VBR)
+ av_log(avctx, AV_LOG_INFO, "VBR ");
+ if (tmp & VA_RC_VCM)
+ av_log(avctx, AV_LOG_INFO, "VCM ");
+ if (tmp & VA_RC_CQP)
+ av_log(avctx, AV_LOG_INFO, "CQP ");
+ if (tmp & VA_RC_VBR_CONSTRAINED)
+ av_log(avctx, AV_LOG_INFO, "VBR_CONSTRAINED ");
+
+ av_log(avctx, AV_LOG_INFO, "\n");
+
+ /* need to check if support rc_mode */
+ ictx->config_attrib[ictx->config_attrib_num].type = VAConfigAttribRateControl;
+ ictx->config_attrib[ictx->config_attrib_num].value = ictx->rc_mode;
+ ictx->config_attrib_num++;
+ }
+
+
+ if (ictx->attrib[VAConfigAttribEncPackedHeaders].value != VA_ATTRIB_NOT_SUPPORTED) {
+ int tmp = ictx->attrib[VAConfigAttribEncPackedHeaders].value;
+
+ av_log(avctx, AV_LOG_INFO, "Support VAConfigAttribEncPackedHeaders\n");
+
+ ictx->h264_packedheader = 1;
+ ictx->config_attrib[ictx->config_attrib_num].type = VAConfigAttribEncPackedHeaders;
+ ictx->config_attrib[ictx->config_attrib_num].value = VA_ENC_PACKED_HEADER_NONE;
+
+ if (tmp & VA_ENC_PACKED_HEADER_SEQUENCE) {
+ av_log(avctx, AV_LOG_INFO, "Support packed sequence headers\n");
+ ictx->config_attrib[ictx->config_attrib_num].value |= VA_ENC_PACKED_HEADER_SEQUENCE;
+ }
+
+ if (tmp & VA_ENC_PACKED_HEADER_PICTURE) {
+ av_log(avctx, AV_LOG_INFO, "Support packed picture headers\n");
+ ictx->config_attrib[ictx->config_attrib_num].value |= VA_ENC_PACKED_HEADER_PICTURE;
+ }
+
+ if (tmp & VA_ENC_PACKED_HEADER_SLICE) {
+ av_log(avctx, AV_LOG_INFO, "Support packed slice headers\n");
+ ictx->config_attrib[ictx->config_attrib_num].value |= VA_ENC_PACKED_HEADER_SLICE;
+ }
+
+ if (tmp & VA_ENC_PACKED_HEADER_MISC) {
+ av_log(avctx, AV_LOG_INFO, "Support packed misc headers\n");
+ ictx->config_attrib[ictx->config_attrib_num].value |= VA_ENC_PACKED_HEADER_MISC;
+ }
+
+ ictx->enc_packed_header_idx = ictx->config_attrib_num;
+ ictx->config_attrib_num++;
+ }
+
+ if (ictx->attrib[VAConfigAttribEncInterlaced].value != VA_ATTRIB_NOT_SUPPORTED) {
+ int tmp = ictx->attrib[VAConfigAttribEncInterlaced].value;
+
+ av_log(avctx, AV_LOG_INFO, "Support VAConfigAttribEncInterlaced\n");
+
+ if (tmp & VA_ENC_INTERLACED_FRAME)
+ av_log(avctx, AV_LOG_INFO, "support VA_ENC_INTERLACED_FRAME\n");
+ if (tmp & VA_ENC_INTERLACED_FIELD)
+ av_log(avctx, AV_LOG_INFO, "Support VA_ENC_INTERLACED_FIELD\n");
+ if (tmp & VA_ENC_INTERLACED_MBAFF)
+ av_log(avctx, AV_LOG_INFO, "Support VA_ENC_INTERLACED_MBAFF\n");
+ if (tmp & VA_ENC_INTERLACED_PAFF)
+ av_log(avctx, AV_LOG_INFO, "Support VA_ENC_INTERLACED_PAFF\n");
+
+ ictx->config_attrib[ictx->config_attrib_num].type = VAConfigAttribEncInterlaced;
+ ictx->config_attrib[ictx->config_attrib_num].value = VA_ENC_PACKED_HEADER_NONE;
+ ictx->config_attrib_num++;
+ }
+
+ if (ictx->attrib[VAConfigAttribEncMaxRefFrames].value != VA_ATTRIB_NOT_SUPPORTED) {
+ ictx->h264_maxref = ictx->attrib[VAConfigAttribEncMaxRefFrames].value;
+
+ av_log(avctx, AV_LOG_INFO, "Support %d RefPicList0 and %d RefPicList1\n",
+ ictx->h264_maxref & 0xffff, (ictx->h264_maxref >> 16) & 0xffff );
+ }
+
+ if (ictx->attrib[VAConfigAttribEncMaxSlices].value != VA_ATTRIB_NOT_SUPPORTED)
+ av_log(avctx, AV_LOG_INFO, "Support %d slices\n", ictx->attrib[VAConfigAttribEncMaxSlices].value);
+
+ if (ictx->attrib[VAConfigAttribEncSliceStructure].value != VA_ATTRIB_NOT_SUPPORTED) {
+ int tmp = ictx->attrib[VAConfigAttribEncSliceStructure].value;
+
+ av_log(avctx, AV_LOG_INFO, "Support VAConfigAttribEncSliceStructure\n");
+
+ if (tmp & VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS)
+ av_log(avctx, AV_LOG_INFO, "Support VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS\n");
+ if (tmp & VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS)
+ av_log(avctx, AV_LOG_INFO, "Support VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS\n");
+ if (tmp & VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS)
+ av_log(avctx, AV_LOG_INFO, "Support VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS\n");
+ }
+ if (ictx->attrib[VAConfigAttribEncMacroblockInfo].value != VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_INFO, "Support VAConfigAttribEncMacroblockInfo\n");
+ }
+
+ free(entrypoints);
+ return 0;
+}
+
+static int setup_encode(AVCodecContext *avctx, VaapiH264EncContext *ictx)
+{
+ VAStatus va_status;
+ VASurfaceID *tmp_surfaceid;
+ int codedbuf_size, i;
+
+ va_status = vaCreateConfig(ictx->va_dpy, ictx->profile, VAEntrypointEncSlice,
+ &ictx->config_attrib[0], ictx->config_attrib_num, &ictx->config_id);
+ CHECK_VASTATUS(avctx, va_status, "vaCreateConfig");
+
+ ictx->frame_width_mbaligned = (avctx->width + 15) & (~15);
+ ictx->frame_height_mbaligned = (avctx->height + 15) & (~15);
+ if (avctx->width != ictx->frame_width_mbaligned ||
+ avctx->height != ictx->frame_height_mbaligned) {
+ av_log( avctx, AV_LOG_INFO,
+ "Source frame is %dx%d and will code clip to %dx%d with crop\n",
+ avctx->width, avctx->height,
+ ictx->frame_width_mbaligned, ictx->frame_height_mbaligned);
+ }
+
+ /* create source surfaces */
+ va_status = vaCreateSurfaces(ictx->va_dpy, VA_RT_FORMAT_YUV420,
+ ictx->frame_width_mbaligned, ictx->frame_height_mbaligned,
+ &ictx->src_surface_id[0], SURFACE_NUM,
+ NULL, 0);
+ CHECK_VASTATUS(avctx, va_status, "vaCreateSurfaces");
+
+ /* create reference surfaces */
+ va_status = vaCreateSurfaces(ictx->va_dpy, VA_RT_FORMAT_YUV420,
+ ictx->frame_width_mbaligned, ictx->frame_height_mbaligned,
+ &ictx->ref_surface[0], SURFACE_NUM, NULL, 0);
+
+ CHECK_VASTATUS(avctx, va_status, "vaCreateSurfaces");
+
+ tmp_surfaceid = calloc(2 * SURFACE_NUM, sizeof(VASurfaceID));
+ memcpy(tmp_surfaceid, ictx->src_surface_id, SURFACE_NUM * sizeof(VASurfaceID));
+ memcpy(tmp_surfaceid + SURFACE_NUM, ictx->ref_surface, SURFACE_NUM * sizeof(VASurfaceID));
+
+ /* Create a context for this encode pipe */
+ va_status = vaCreateContext(ictx->va_dpy, ictx->config_id,
+ ictx->frame_width_mbaligned, ictx->frame_height_mbaligned,
+ VA_PROGRESSIVE,
+ tmp_surfaceid, 2 * SURFACE_NUM,
+ &ictx->context_id);
+ CHECK_VASTATUS(avctx, va_status, "vaCreateContext");
+ free(tmp_surfaceid);
+
+ codedbuf_size = (ictx->frame_width_mbaligned * ictx->frame_height_mbaligned * 400) / (16*16);
+
+ for (i = 0; i < SURFACE_NUM; i++) {
+ /* create coded buffer once for all
+ * other VA buffers which won't be used again after vaRenderPicture.
+ * so APP can always vaCreateBuffer for every frame
+ * but coded buffer need to be mapped and accessed after vaRenderPicture/vaEndPicture
+ * so VA won't maintain the coded buffer
+ */
+ va_status = vaCreateBuffer(ictx->va_dpy, ictx->context_id, VAEncCodedBufferType,
+ codedbuf_size, 1, NULL, &ictx->coded_buf[i]);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+ }
+
+ memset(&ictx->seq_param, 0, sizeof(ictx->seq_param));
+ memset(&ictx->pic_param, 0, sizeof(ictx->pic_param));
+
+ return 0;
+}
+
+static int release_encode(VaapiH264EncContext *ictx)
+{
+ int i;
+
+ vaDestroySurfaces(ictx->va_dpy, &ictx->src_surface_id[0], SURFACE_NUM);
+ vaDestroySurfaces(ictx->va_dpy, &ictx->ref_surface[0], SURFACE_NUM);
+
+ for (i = 0; i < SURFACE_NUM; i++)
+ vaDestroyBuffer(ictx->va_dpy, ictx->coded_buf[i]);
+
+ vaDestroyContext(ictx->va_dpy, ictx->context_id);
+ vaDestroyConfig(ictx->va_dpy, ictx->config_id);
+
+ return 0;
+}
+
+static int deinit_va(VaapiH264EncContext *ictx)
+{
+ vaTerminate(ictx->va_dpy);
+
+ ff_va_close_display(ictx->va_dpy);
+
+ return 0;
+}
+
+static int load_surface(AVCodecContext *avctx, VaapiH264EncContext *ictx, VASurface *surface, int frame_no, const AVFrame *frame)
+{
+ VAImage surface_image;
+ uint8_t *surface_p = NULL;
+ VAStatus va_status;
+ VASurfaceID surface_id = *(surface->surface_id);
+ enum AVPixelFormat dst_pix_fmt;
+ uint8_t *dst_planes[3];
+ struct SwsContext *sws_ctx = NULL;
+
+ surface->frame_num = frame_no;
+
+ va_status = vaDeriveImage(ictx->va_dpy, surface_id, &surface_image);
+ CHECK_VASTATUS(avctx, va_status,"vaDeriveImage");
+
+ va_status = vaMapBuffer(ictx->va_dpy, surface_image.buf, (void **)&surface_p);
+ assert(VA_STATUS_SUCCESS == va_status);
+
+ switch (surface_image.format.fourcc) {
+ case VA_FOURCC_NV12:
+ dst_pix_fmt = AV_PIX_FMT_NV12;
+ break;
+ case VA_FOURCC_IYUV:
+ dst_pix_fmt = AV_PIX_FMT_YUV420P;
+ break;
+ default:
+ return -1;
+ }
+
+ dst_planes[0] = surface_p + surface_image.offsets[0];
+ dst_planes[1] = surface_p + surface_image.offsets[1];
+ dst_planes[2] = surface_p + surface_image.offsets[2];
+
+ sws_ctx = sws_getContext( frame->width, frame->height, frame->format,
+ frame->width, frame->height, dst_pix_fmt,
+ 0, NULL, NULL, NULL );
+ if (!sws_ctx) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Impossible to create scale context for the conversion "
+ "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n",
+ av_get_pix_fmt_name(frame->format), frame->width, frame->height,
+ av_get_pix_fmt_name(dst_pix_fmt), frame->width, frame->height);
+ return AVERROR(EINVAL);
+ }
+
+ sws_scale( sws_ctx, (const uint8_t * const*)frame->data, frame->linesize,
+ 0, frame->height, dst_planes, surface_image.pitches );
+
+ sws_freeContext(sws_ctx);
+
+ vaUnmapBuffer(ictx->va_dpy, surface_image.buf);
+
+ vaDestroyImage(ictx->va_dpy, surface_image.image_id);
+
+ return 0;
+}
+
+static void encoding2display_order(VaapiH264EncContext *ictx,
+ unsigned long long encoding_order,int intra_period,
+ int intra_idr_period,int ip_period,
+ unsigned long long *displaying_order,
+ int *frame_type)
+{
+ int encoding_order_gop = encoding_order % intra_period;
+
+ /* new sequence like
+ * IDR PPPPP IPPPPP
+ * IDR (PBB)(PBB)(IBB)(PBB)
+ */
+
+ if (encoding_order % intra_idr_period == 0) { /* the first frame */
+ *frame_type = FRAME_IDR;
+ *displaying_order = encoding_order;
+ } else if(encoding_order_gop % intra_period == 0) {
+ *frame_type = FRAME_I;
+ *displaying_order = encoding_order;
+ } else if(ictx->nb_surfaces_loaded < ip_period) {
+ *frame_type = FRAME_P;
+ *displaying_order = encoding_order;
+ } else if (((encoding_order_gop - 1) % ip_period) != 0) { /* B frames */
+ *frame_type = FRAME_B;
+ *displaying_order = encoding_order - 1;
+
+ if(ictx->last_p + ip_period-1 >= (intra_period * ((encoding_order / intra_period)+1))) {
+ *frame_type = FRAME_P;
+ *displaying_order = encoding_order;
+ ictx->last_p = encoding_order;
+ }
+ } else {
+ *frame_type = FRAME_P;
+ *displaying_order = encoding_order + ip_period - 1;
+
+ if(*displaying_order >= (intra_period * ((encoding_order / intra_period)+1)))
+ *displaying_order = encoding_order;
+
+ ictx->last_p = encoding_order;
+ }
+}
+
+static int render_packedsequence(AVCodecContext *avctx, VaapiH264EncContext *ictx)
+{
+ VAEncPackedHeaderParameterBuffer packedheader_param_buffer;
+ VABufferID packedseq_para_bufid, packedseq_data_bufid, render_id[2];
+ unsigned int length_in_bits;
+ unsigned char *packedseq_buffer = NULL;
+ VAStatus va_status;
+
+ length_in_bits = build_packed_seq_buffer(ictx, &packedseq_buffer);
+
+ packedheader_param_buffer.type = VAEncPackedHeaderSequence;
+
+ packedheader_param_buffer.bit_length = length_in_bits; /*length_in_bits*/
+ packedheader_param_buffer.has_emulation_bytes = 0;
+ va_status = vaCreateBuffer(ictx->va_dpy,
+ ictx->context_id,
+ VAEncPackedHeaderParameterBufferType,
+ sizeof(packedheader_param_buffer), 1, &packedheader_param_buffer,
+ &packedseq_para_bufid);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+
+ va_status = vaCreateBuffer(ictx->va_dpy,
+ ictx->context_id,
+ VAEncPackedHeaderDataBufferType,
+ (length_in_bits + 7) / 8, 1, packedseq_buffer,
+ &packedseq_data_bufid);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+
+ render_id[0] = packedseq_para_bufid;
+ render_id[1] = packedseq_data_bufid;
+ va_status = vaRenderPicture(ictx->va_dpy, ictx->context_id, render_id, 2);
+ CHECK_VASTATUS(avctx, va_status,"vaRenderPicture");
+
+ free(packedseq_buffer);
+
+ return 0;
+}
+
+static int render_packedpicture(AVCodecContext *avctx, VaapiH264EncContext *ictx)
+{
+ VAEncPackedHeaderParameterBuffer packedheader_param_buffer;
+ VABufferID packedpic_para_bufid, packedpic_data_bufid, render_id[2];
+ unsigned int length_in_bits;
+ unsigned char *packedpic_buffer = NULL;
+ VAStatus va_status;
+
+ length_in_bits = build_packed_pic_buffer(ictx, &packedpic_buffer);
+ packedheader_param_buffer.type = VAEncPackedHeaderPicture;
+ packedheader_param_buffer.bit_length = length_in_bits;
+ packedheader_param_buffer.has_emulation_bytes = 0;
+
+ va_status = vaCreateBuffer(ictx->va_dpy,
+ ictx->context_id,
+ VAEncPackedHeaderParameterBufferType,
+ sizeof(packedheader_param_buffer), 1, &packedheader_param_buffer,
+ &packedpic_para_bufid);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+
+ va_status = vaCreateBuffer(ictx->va_dpy,
+ ictx->context_id,
+ VAEncPackedHeaderDataBufferType,
+ (length_in_bits + 7) / 8, 1, packedpic_buffer,
+ &packedpic_data_bufid);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+
+ render_id[0] = packedpic_para_bufid;
+ render_id[1] = packedpic_data_bufid;
+ va_status = vaRenderPicture(ictx->va_dpy, ictx->context_id, render_id, 2);
+ CHECK_VASTATUS(avctx, va_status,"vaRenderPicture");
+
+ free(packedpic_buffer);
+
+ return 0;
+}
+
+static int render_packedslice(AVCodecContext *avctx, VaapiH264EncContext *ictx)
+{
+ VAEncPackedHeaderParameterBuffer packedheader_param_buffer;
+ VABufferID packedslice_para_bufid, packedslice_data_bufid, render_id[2];
+ unsigned int length_in_bits;
+ unsigned char *packedslice_buffer = NULL;
+ VAStatus va_status;
+
+ length_in_bits = build_packed_slice_buffer(ictx, &packedslice_buffer);
+ packedheader_param_buffer.type = VAEncPackedHeaderSlice;
+ packedheader_param_buffer.bit_length = length_in_bits;
+ packedheader_param_buffer.has_emulation_bytes = 0;
+
+ va_status = vaCreateBuffer(ictx->va_dpy,
+ ictx->context_id,
+ VAEncPackedHeaderParameterBufferType,
+ sizeof(packedheader_param_buffer), 1, &packedheader_param_buffer,
+ &packedslice_para_bufid);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+
+ va_status = vaCreateBuffer(ictx->va_dpy,
+ ictx->context_id,
+ VAEncPackedHeaderDataBufferType,
+ (length_in_bits + 7) / 8, 1, packedslice_buffer,
+ &packedslice_data_bufid);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+
+ render_id[0] = packedslice_para_bufid;
+ render_id[1] = packedslice_data_bufid;
+ va_status = vaRenderPicture(ictx->va_dpy, ictx->context_id, render_id, 2);
+ CHECK_VASTATUS(avctx, va_status, "vaRenderPicture");
+
+ free(packedslice_buffer);
+ return 0;
+}
+
+static int render_slice(AVCodecContext *avctx, VaapiH264EncContext *ictx)
+{
+ VABufferID slice_param_buf;
+ VAStatus va_status;
+ int i;
+
+ update_ref_pic_list(ictx);
+
+ /* one frame, one slice */
+ ictx->slice_param.macroblock_address = 0;
+ ictx->slice_param.num_macroblocks = ictx->frame_width_mbaligned * ictx->frame_height_mbaligned/(16*16); /* Measured by MB */
+ ictx->slice_param.slice_type = (ictx->current_frame_type == FRAME_IDR)?2:ictx->current_frame_type;
+ if (ictx->current_frame_type == FRAME_IDR) {
+ if (ictx->current_frame_encoding != 0)
+ ++ictx->slice_param.idr_pic_id;
+ } else if (ictx->current_frame_type == FRAME_P) {
+ int refpiclist0_max = ictx->h264_maxref & 0xffff;
+ memcpy(ictx->slice_param.RefPicList0, ictx->ref_pic_list0_P, refpiclist0_max*sizeof(VAPictureH264));
+
+ for (i = refpiclist0_max; i < 32; i++) {
+ ictx->slice_param.RefPicList0[i].picture_id = VA_INVALID_SURFACE;
+ ictx->slice_param.RefPicList0[i].flags = VA_PICTURE_H264_INVALID;
+ }
+ } else if (ictx->current_frame_type == FRAME_B) {
+ int refpiclist0_max = ictx->h264_maxref & 0xffff;
+ int refpiclist1_max = (ictx->h264_maxref >> 16) & 0xffff;
+
+ memcpy(ictx->slice_param.RefPicList0, ictx->ref_pic_list0_B, refpiclist0_max*sizeof(VAPictureH264));
+ for (i = refpiclist0_max; i < 32; i++) {
+ ictx->slice_param.RefPicList0[i].picture_id = VA_INVALID_SURFACE;
+ ictx->slice_param.RefPicList0[i].flags = VA_PICTURE_H264_INVALID;
+ }
+
+ memcpy(ictx->slice_param.RefPicList1, ictx->ref_pic_list1_B, refpiclist1_max*sizeof(VAPictureH264));
+ for (i = refpiclist1_max; i < 32; i++) {
+ ictx->slice_param.RefPicList1[i].picture_id = VA_INVALID_SURFACE;
+ ictx->slice_param.RefPicList1[i].flags = VA_PICTURE_H264_INVALID;
+ }
+ }
+
+ ictx->slice_param.slice_alpha_c0_offset_div2 = 0;
+ ictx->slice_param.slice_beta_offset_div2 = 0;
+ ictx->slice_param.direct_spatial_mv_pred_flag = 1;
+ ictx->slice_param.pic_order_cnt_lsb = (ictx->current_frame_display - ictx->current_IDR_display) % MAX_PIC_ORDER_CNT_LSB;
+
+ if(ictx->h264_packedheader && (ictx->config_attrib[ictx->enc_packed_header_idx].value & VA_ENC_PACKED_HEADER_SLICE))
+ render_packedslice(avctx, ictx);
+
+ va_status = vaCreateBuffer(ictx->va_dpy, ictx->context_id, VAEncSliceParameterBufferType,
+ sizeof(ictx->slice_param),1,&ictx->slice_param,&slice_param_buf);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");;
+
+ va_status = vaRenderPicture(ictx->va_dpy, ictx->context_id, &slice_param_buf, 1);
+ CHECK_VASTATUS(avctx, va_status,"vaRenderPicture");
+
+ return 0;
+}
+
+static VASurface* get_surface(VaapiH264EncContext *ictx, int frame_no)
+{
+ int i;
+
+ for(i = 0; i < SURFACE_NUM; i++) {
+ if(ictx->src_sufrace[i].frame_num == frame_no)
+ return &ictx->src_sufrace[i];
+ }
+
+ return NULL;
+}
+
+static int save_output_packet(AVCodecContext *avctx, VaapiH264EncContext *ictx, AVPacket *pkt, int *got_packet)
+{
+ VAStatus va_status;
+ VACodedBufferSegment *buf_list = NULL, *tmp_list = NULL;
+ unsigned int coded_size = 0;
+ int ret = 0;
+ unsigned char *p;
+ VASurface *surface = get_surface(ictx, ictx->current_frame_display);
+ enum AVPictureType pict_type;
+
+ *got_packet = 0;
+ va_status = vaSyncSurface(ictx->va_dpy, *(surface->surface_id));
+ CHECK_VASTATUS(avctx, va_status,"vaSyncSurface");
+
+ va_status = vaMapBuffer(ictx->va_dpy, ictx->coded_buf[ictx->current_frame_display % SURFACE_NUM],(void **)(&buf_list));
+ CHECK_VASTATUS(avctx, va_status,"vaMapBuffer");
+
+ tmp_list = buf_list;
+
+ while (tmp_list != NULL) {
+ coded_size += tmp_list->size;
+ tmp_list = (VACodedBufferSegment *) tmp_list->next;
+ }
+
+ if(coded_size > 0) {
+ if ((ret = ff_alloc_packet2(avctx, pkt, coded_size, 0)) < 0)
+ goto cleanup;
+
+ switch (ictx->current_frame_type) {
+ case FRAME_IDR:
+ pkt->flags |= AV_PKT_FLAG_KEY;
+
+ case FRAME_I:
+ pict_type = AV_PICTURE_TYPE_I;
+ break;
+ case FRAME_P:
+ pict_type = AV_PICTURE_TYPE_P;
+ break;
+ case FRAME_B:
+ pict_type = AV_PICTURE_TYPE_B;
+ break;
+ }
+
+#if FF_API_CODED_FRAME
+FF_DISABLE_DEPRECATION_WARNINGS
+ avctx->coded_frame->pict_type = pict_type;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ pkt->pts = surface->pts;
+ pkt->dts = (ictx->current_frame_encoding - (ictx->ip_period-1))*ictx->duration;
+
+ if(pkt->pts < pkt->dts)
+ pkt->pts = pkt->dts;
+
+ *got_packet = 1;
+ } else {
+ goto cleanup;
+ }
+
+ p = pkt->data;
+
+ while (buf_list != NULL) {
+ memcpy(p, buf_list->buf, buf_list->size);
+ p += buf_list->size;
+ buf_list = (VACodedBufferSegment *) buf_list->next;
+ }
+
+cleanup:
+ vaUnmapBuffer(ictx->va_dpy, ictx->coded_buf[ictx->current_frame_display % SURFACE_NUM]);
+
+ return ret;
+}
+
+static int update_reference_frames(VaapiH264EncContext *ictx)
+{
+ int i;
+
+ if (ictx->current_frame_type == FRAME_B)
+ return 0;
+
+ ictx->current_curr_pic.flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE;
+ ictx->num_short_term++;
+ if (ictx->num_short_term > ictx->num_ref_frames)
+ ictx->num_short_term = ictx->num_ref_frames;
+ for (i=ictx->num_short_term-1; i>0; i--)
+ ictx->reference_frames[i] = ictx->reference_frames[i-1];
+ ictx->reference_frames[0] = ictx->current_curr_pic;
+
+ if (ictx->current_frame_type != FRAME_B)
+ ictx->current_frame_num++;
+ if (ictx->current_frame_num > MAX_FRAME_NUM)
+ ictx->current_frame_num = 0;
+
+ return 0;
+}
+
+static int build_packed_sei_buffer_timing(VaapiH264EncContext *ictx, unsigned char **sei_buffer)
+{
+ VaapiH264EncBitstream sei_bs;
+
+ ff_vaapi_h264enc_bs_start(&sei_bs);
+ ff_vaapi_h264enc_nal_start_code_prefix(&sei_bs);
+ ff_vaapi_h264enc_nal_header(&sei_bs, NAL_REF_IDC_NONE, NAL_SEI);
+ ff_vaapi_h264enc_sei_rbsp(ictx, &sei_bs);
+ ff_vaapi_h264enc_bs_end(&sei_bs);
+
+ *sei_buffer = (unsigned char *)sei_bs.buffer;
+
+ return sei_bs.bit_offset;
+}
+
+static int update_sei_param(AVCodecContext* avctx, VaapiH264EncContext* ictx)
+{
+ VAEncPackedHeaderParameterBuffer packed_header_param_buffer;
+ unsigned int length_in_bits;
+ unsigned char *packed_sei_buffer = NULL;
+ VAStatus va_status;
+ VABufferID packed_sei_header_param_buf_id, packed_sei_buf_id, buffId[2];
+
+ length_in_bits = build_packed_sei_buffer_timing(ictx, &packed_sei_buffer);
+
+ packed_header_param_buffer.type = VAEncPackedHeaderH264_SEI;
+ packed_header_param_buffer.bit_length = length_in_bits;
+ packed_header_param_buffer.has_emulation_bytes = 0;
+
+ va_status = vaCreateBuffer(ictx->va_dpy, ictx->context_id,
+ VAEncPackedHeaderParameterBufferType,
+ sizeof(packed_header_param_buffer), 1, &packed_header_param_buffer,
+ &packed_sei_header_param_buf_id);
+
+ CHECK_VASTATUS(avctx, va_status, "vaCreateBuffer");
+
+ va_status = vaCreateBuffer(ictx->va_dpy, ictx->context_id,
+ VAEncPackedHeaderDataBufferType,
+ (length_in_bits + 7) / 8, 1, packed_sei_buffer,
+ &packed_sei_buf_id);
+ CHECK_VASTATUS(avctx, va_status,"vaCreateBuffer");
+
+ buffId[0] = packed_sei_header_param_buf_id;
+ buffId[1] = packed_sei_buf_id;
+
+ va_status = vaRenderPicture(ictx->va_dpy, ictx->context_id, &buffId[0], 2);
+ CHECK_VASTATUS(avctx, va_status,"vaRenderPicture");
+
+ free(packed_sei_buffer);
+ return 0;
+}
+
+static av_cold int vaapi_h264enc_init(AVCodecContext *avctx)
+{
+ int ret, i;
+ VaapiH264EncContext *ictx = avctx->priv_data;
+
+ vaapi_h264enc_param_default(avctx, ictx);
+
+ for(i = 0; i < SURFACE_NUM; i++)
+ ictx->src_sufrace[i].surface_id = &ictx->src_surface_id[i];
+
+ ret = init_va(avctx, ictx);
+ if(ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Initialization failed in function init_va()\n");
+ return -1;
+ }
+
+ ret = setup_encode(avctx, ictx);
+
+ init_sps(avctx, ictx, &ictx->seq_param);
+ init_pps(ictx);
+
+ if(ictx->frame_bitrate > 0)
+ init_sei(ictx);
+
+ if (avctx->flags & CODEC_FLAG_GLOBAL_HEADER) {
+ int sps_size, pps_size;
+ unsigned char *sps, *pps;
+
+ sps_size = (build_packed_seq_buffer(ictx, &sps) + 7) / 8;
+ avctx->extradata_size = sps_size;
+
+ pps_size = (build_packed_pic_buffer(ictx, &pps) + 7) / 8;
+ avctx->extradata_size += pps_size;
+
+ avctx->extradata = av_malloc(avctx->extradata_size);
+
+ memcpy(avctx->extradata, sps, sps_size);
+ memcpy(avctx->extradata+sps_size, pps, pps_size);
+
+ free(sps);
+ free(pps);
+ }
+
+ if(ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Initialization failed in function setup_encode()\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int vaapi_h264enc_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet)
+{
+ VAStatus va_status;
+ VaapiH264EncContext *ictx = avctx->priv_data;
+ VASurface *surface;
+
+ if(!ictx->nb_surfaces_loaded && !frame) {
+ *got_packet = 0;
+ return 0;
+ }
+
+ if(ictx->nb_surfaces_loaded < SURFACE_NUM && frame) {
+ load_surface(avctx, ictx, &ictx->src_sufrace[ictx->nb_surfaces_loaded], ictx->nb_surfaces_loaded, frame);
+
+ ictx->src_sufrace[ictx->nb_surfaces_loaded].pts = frame->pts;
+
+ if(ictx->nb_surfaces_loaded >= 1)
+ ictx->duration = ictx->src_sufrace[ictx->nb_surfaces_loaded].pts - ictx->src_sufrace[ictx->nb_surfaces_loaded-1].pts;
+
+ ictx->nb_surfaces_loaded++;
+ *got_packet = 0;
+ return 0;
+ }
+
+ memset(&ictx->pic_param, 0, sizeof(ictx->pic_param));
+ memset(&ictx->slice_param, 0, sizeof(ictx->slice_param));
+
+ encoding2display_order(ictx, ictx->current_frame_encoding, ictx->intra_period, ictx->intra_idr_period,
+ ictx->ip_period, &ictx->current_frame_display, &ictx->current_frame_type);
+ if (ictx->current_frame_type == FRAME_IDR) {
+ ictx->num_short_term = 0;
+ ictx->current_frame_num = 0;
+ ictx->current_IDR_display = ictx->current_frame_display;
+ }
+
+ surface = get_surface(ictx, ictx->current_frame_display);
+
+ va_status = vaBeginPicture(ictx->va_dpy, ictx->context_id, *(surface->surface_id));
+ CHECK_VASTATUS(avctx, va_status,"vaBeginPicture");
+
+ if (ictx->current_frame_type == FRAME_IDR) {
+ render_sequence(avctx, ictx);
+
+ if(ictx->frame_bitrate > 0)
+ update_sei_param(avctx, ictx);
+
+ render_picture(avctx, ictx);
+ if (ictx->h264_packedheader) {
+ render_packedsequence(avctx, ictx);
+ render_packedpicture(avctx, ictx);
+ }
+ } else {
+ if(ictx->frame_bitrate > 0)
+ update_sei_param(avctx, ictx);
+
+ render_picture(avctx, ictx);
+ }
+
+ render_slice(avctx, ictx);
+
+ va_status = vaEndPicture(ictx->va_dpy, ictx->context_id);
+ CHECK_VASTATUS(avctx, va_status,"vaEndPicture");;
+
+ save_output_packet(avctx, ictx, pkt, got_packet);
+
+ update_reference_frames(ictx);
+
+ if(frame != NULL) {
+ load_surface(avctx, ictx, surface, ictx->current_frame_encoding+SURFACE_NUM, frame);
+ surface->pts = frame->pts;
+ } else
+ ictx->nb_surfaces_loaded--;
+
+ ictx->current_frame_encoding++;
+
+ return 0;
+}
+
+static av_cold int vaapi_h264enc_close(AVCodecContext *avctx)
+{
+ VaapiH264EncContext *ictx = avctx->priv_data;
+
+ if(ictx == NULL)
+ return -1;
+
+ if(avctx->extradata)
+ av_freep(&avctx->extradata);
+
+ release_encode(ictx);
+ deinit_va(ictx);
+
+ return 0;
+}
+
+static av_cold void vaapi_h264enc_init_static(AVCodec *codec)
+{
+}
+
+#define OFFSET(x) offsetof(VaapiH264EncContext, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "intra_period", "Set I frame period", OFFSET(intra_period), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE},
+ { "rc_mode", "Rate control method", OFFSET(rc_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, "rc_mode"},
+ { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VA_RC_NONE}, INT_MIN, INT_MAX, VE, "rc_mode"},
+ { "cbr", "Constant bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = VA_RC_CBR}, INT_MIN, INT_MAX, VE, "rc_mode"},
+ { "vbr", "Variable bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = VA_RC_VBR}, INT_MIN, INT_MAX, VE, "rc_mode"},
+ { "vcm", "Video conference mode", 0, AV_OPT_TYPE_CONST, {.i64 = VA_RC_VCM}, INT_MIN, INT_MAX, VE, "rc_mode"},
+ { "cqp", "Constant QP", 0, AV_OPT_TYPE_CONST, {.i64 = VA_RC_CQP}, INT_MIN, INT_MAX, VE, "rc_mode"},
+ { "vbr_constrained", "VBR with peak rate higher than avg", 0, AV_OPT_TYPE_CONST, {.i64 = VA_RC_VBR_CONSTRAINED}, INT_MIN, INT_MAX, VE, "rc_mode"},
+ { "h264_profile", "H264 Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = VAProfileH264High }, INT_MIN, INT_MAX, VE, "profile"},
+ { "baseline_const", "Constrained Baseline profile", 0, AV_OPT_TYPE_CONST, { .i64 = VAProfileH264ConstrainedBaseline }, INT_MIN, INT_MAX, VE, "profile"},
+ { "main", "Main profile", 0, AV_OPT_TYPE_CONST, { .i64 = VAProfileH264Main }, INT_MIN, INT_MAX, VE, "profile"},
+ { "high", "High profile", 0, AV_OPT_TYPE_CONST, { .i64 = VAProfileH264High }, INT_MIN, INT_MAX, VE, "profile"},
+ { "h264_coder", "Entropy coder", OFFSET(entropy_coder), AV_OPT_TYPE_INT, { .i64 = H264_CODER_CABAC }, INT_MIN, INT_MAX, VE, "entropy_coder"},
+ { "cabac", "cabac", 0, AV_OPT_TYPE_CONST, { .i64 = H264_CODER_CABAC }, INT_MIN, INT_MAX, VE, "entropy_coder"},
+ { "cavlc", "cavlc", 0, AV_OPT_TYPE_CONST, { .i64 = H264_CODER_CAVLC }, INT_MIN, INT_MAX, VE, "entropy_coder"},
+ { "display", "Display device to use", OFFSET(display), AV_OPT_TYPE_INT, { .i64 = VA_DISPLAY_DRM }, INT_MIN, INT_MAX, VE, "display"},
+ { "drm", "libdrm", 0, AV_OPT_TYPE_CONST, { .i64 = VA_DISPLAY_DRM }, INT_MIN, INT_MAX, VE, "display"},
+ { "x11", "X Window system", 0, AV_OPT_TYPE_CONST, { .i64 = VA_DISPLAY_X11 }, INT_MIN, INT_MAX, VE, "display"},
+ { NULL },
+};
+
+static const AVClass vaapi_h264enc_class = {
+ .class_name = "vaapi_h264enc",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static const AVCodecDefault vaapi_h264enc_defaults[] = {
+ { "b", "0" },
+ { "bf", "-1" },
+ { "g", "-1" },
+ { "qmin", "-1" },
+ { NULL },
+};
+
+static const enum AVPixelFormat pix_fmts[] = {
+ AV_PIX_FMT_YUV420P,
+ AV_PIX_FMT_NONE
+};
+
+AVCodec ff_vaapi_h264_encoder = {
+ .name = "h264enc_vaapi",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .id = AV_CODEC_ID_H264,
+ .priv_data_size = sizeof(VaapiH264EncContext),
+ .init = vaapi_h264enc_init,
+ .encode2 = vaapi_h264enc_frame,
+ .close = vaapi_h264enc_close,
+ .capabilities = CODEC_CAP_DELAY,
+ .long_name = NULL_IF_CONFIG_SMALL("VAAPI Encoder H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
+ .priv_class = &vaapi_h264enc_class,
+ .defaults = vaapi_h264enc_defaults,
+ .init_static_data = vaapi_h264enc_init_static,
+ .pix_fmts = pix_fmts
+};
diff --git a/libavcodec/vaapi_h264enc.h b/libavcodec/vaapi_h264enc.h
new file mode 100644
index 0000000..e6abcd3
--- /dev/null
+++ b/libavcodec/vaapi_h264enc.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 Bryan Christ
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VAAPI_H264ENC_H
+#define AVCODEC_VAAPI_H264ENC_H
+
+#include <va/va.h>
+#include <va/va_enc_h264.h>
+
+#include "vaapi_display.h"
+#include "avcodec.h"
+
+
+#define SURFACE_NUM 16 /* 16 surfaces for source YUV */
+
+#define H264_CODER_CAVLC 0
+#define H264_CODER_CABAC 1
+
+
+typedef struct VASurface
+{
+ VASurfaceID *surface_id;
+ unsigned int frame_num;
+ int64_t pts;
+} VASurface;
+
+typedef struct VaapiH264EncContext
+{
+ AVClass *class;
+ VADisplay va_dpy;
+ VADisplayType display;
+ VAProfile profile;
+ VAConfigAttrib attrib[VAConfigAttribTypeMax];
+ VAConfigAttrib config_attrib[VAConfigAttribTypeMax];
+ VAConfigID config_id;
+ VAContextID context_id;
+ VASurfaceID src_surface_id[SURFACE_NUM];
+ VASurface src_sufrace[SURFACE_NUM];
+ VABufferID coded_buf[SURFACE_NUM];
+ VASurfaceID ref_surface[SURFACE_NUM];
+
+ VAEncSequenceParameterBufferH264 seq_param;
+ VAEncPictureParameterBufferH264 pic_param;
+ VAEncSliceParameterBufferH264 slice_param;
+
+ int intra_period;
+ int intra_idr_period;
+ int ip_period;
+ int b_frames;
+ int constraint_set_flag;
+ int entropy_coder;
+ int rc_mode;
+ int config_attrib_num;
+ int enc_packed_header_idx;
+ int h264_packedheader;
+ int h264_maxref;
+ int frame_bitrate;
+ int initial_qp;
+ int minimal_qp;
+
+ unsigned int num_ref_frames;
+
+ int frame_width_mbaligned;
+ int frame_height_mbaligned;
+
+ int current_frame_type;
+
+ unsigned long long current_frame_encoding;
+ unsigned long long current_frame_display;
+ unsigned long long current_frame_num;
+ unsigned long long current_IDR_display;
+ unsigned long long last_p;
+
+ VAPictureH264 current_curr_pic;
+ VAPictureH264 reference_frames[16];
+ VAPictureH264 ref_pic_list0_P[32];
+ VAPictureH264 ref_pic_list0_B[32];
+ VAPictureH264 ref_pic_list1_B[32];
+
+ unsigned int num_short_term;
+
+ int nb_surfaces_loaded;
+
+ int initial_cpb_removal_delay;
+ int initial_cpb_removal_delay_length;
+ int cpb_removal_delay;
+ int cpb_removal_delay_length;
+ int dpb_output_delay_length;
+
+ int64_t duration;
+
+} VaapiH264EncContext;
+
+#endif /* AVCODEC_VAAPI_H264ENC_H */
diff --git a/libavcodec/vaapi_h264enc_paramset.c b/libavcodec/vaapi_h264enc_paramset.c
new file mode 100644
index 0000000..5a8cdc9
--- /dev/null
+++ b/libavcodec/vaapi_h264enc_paramset.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2015 Bryan Christ
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "vaapi_h264enc_paramset.h"
+
+
+static unsigned int
+va_swap32(unsigned int val)
+{
+ unsigned char *pval = (unsigned char *)&val;
+
+ return ((pval[0] << 24) |
+ (pval[1] << 16) |
+ (pval[2] << 8) |
+ (pval[3] << 0));
+}
+
+void ff_vaapi_h264enc_bs_start(VaapiH264EncBitstream *bs)
+{
+ bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING;
+ bs->buffer = calloc(bs->max_size_in_dword * sizeof(int), 1);
+ bs->bit_offset = 0;
+}
+
+void ff_vaapi_h264enc_bs_end(VaapiH264EncBitstream *bs)
+{
+ int pos = (bs->bit_offset >> 5);
+ int bit_offset = (bs->bit_offset & 0x1f);
+ int bit_left = 32 - bit_offset;
+
+ if (bit_offset) {
+ bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left));
+ }
+}
+
+static void
+bitstream_put_ui(VaapiH264EncBitstream *bs, unsigned int val, int size_in_bits)
+{
+ int pos = (bs->bit_offset >> 5);
+ int bit_offset = (bs->bit_offset & 0x1f);
+ int bit_left = 32 - bit_offset;
+
+ if (!size_in_bits)
+ return;
+
+ bs->bit_offset += size_in_bits;
+
+ if (bit_left > size_in_bits) {
+ bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val);
+ } else {
+ size_in_bits -= bit_left;
+ bs->buffer[pos] = (bs->buffer[pos] << bit_left) | (val >> size_in_bits);
+ bs->buffer[pos] = va_swap32(bs->buffer[pos]);
+
+ if (pos + 1 == bs->max_size_in_dword) {
+ bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING;
+ bs->buffer = realloc(bs->buffer, bs->max_size_in_dword * sizeof(unsigned int));
+ }
+
+ bs->buffer[pos + 1] = val;
+ }
+}
+
+static void
+bitstream_put_ue(VaapiH264EncBitstream *bs, unsigned int val)
+{
+ int size_in_bits = 0;
+ int tmp_val = ++val;
+
+ while (tmp_val) {
+ tmp_val >>= 1;
+ size_in_bits++;
+ }
+
+ bitstream_put_ui(bs, 0, size_in_bits - 1); // leading zero
+ bitstream_put_ui(bs, val, size_in_bits);
+}
+
+static void
+bitstream_put_se(VaapiH264EncBitstream *bs, int val)
+{
+ unsigned int new_val;
+
+ if (val <= 0)
+ new_val = -2 * val;
+ else
+ new_val = 2 * val - 1;
+
+ bitstream_put_ue(bs, new_val);
+}
+
+static void
+bitstream_byte_aligning(VaapiH264EncBitstream *bs, int bit)
+{
+ int bit_offset = (bs->bit_offset & 0x7);
+ int bit_left = 8 - bit_offset;
+ int new_val;
+
+ if (!bit_offset)
+ return;
+
+ assert(bit == 0 || bit == 1);
+
+ if (bit)
+ new_val = (1 << bit_left) - 1;
+ else
+ new_val = 0;
+
+ bitstream_put_ui(bs, new_val, bit_left);
+}
+
+static void
+rbsp_trailing_bits(VaapiH264EncBitstream *bs)
+{
+ bitstream_put_ui(bs, 1, 1);
+ bitstream_byte_aligning(bs, 0);
+}
+
+void ff_vaapi_h264enc_nal_start_code_prefix(VaapiH264EncBitstream *bs)
+{
+ bitstream_put_ui(bs, 0x00000001, 32);
+}
+
+void ff_vaapi_h264enc_nal_header(VaapiH264EncBitstream *bs, int nal_ref_idc, int nal_unit_type)
+{
+ bitstream_put_ui(bs, 0, 1); /* forbidden_zero_bit: 0 */
+ bitstream_put_ui(bs, nal_ref_idc, 2);
+ bitstream_put_ui(bs, nal_unit_type, 5);
+}
+
+void ff_vaapi_h264enc_sps_rbsp(VaapiH264EncContext *ctx, VAEncSequenceParameterBufferH264 *seq_param, VaapiH264EncBitstream *bs)
+{
+ int profile_idc = PROFILE_IDC_BASELINE;
+
+ if (ctx->profile == VAProfileH264High)
+ profile_idc = PROFILE_IDC_HIGH;
+ else if (ctx->profile == VAProfileH264Main)
+ profile_idc = PROFILE_IDC_MAIN;
+
+ bitstream_put_ui(bs, profile_idc, 8); /* profile_idc */
+ bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 1), 1); /* constraint_set0_flag */
+ bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 2), 1); /* constraint_set1_flag */
+ bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 4), 1); /* constraint_set2_flag */
+ bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 8), 1); /* constraint_set3_flag */
+ bitstream_put_ui(bs, 0, 4); /* reserved_zero_4bits */
+ bitstream_put_ui(bs, seq_param->level_idc, 8); /* level_idc */
+ bitstream_put_ue(bs, seq_param->seq_parameter_set_id); /* seq_parameter_set_id */
+
+ if ( profile_idc == PROFILE_IDC_HIGH) {
+ bitstream_put_ue(bs, 1); /* chroma_format_idc = 1, 4:2:0 */
+ bitstream_put_ue(bs, 0); /* bit_depth_luma_minus8 */
+ bitstream_put_ue(bs, 0); /* bit_depth_chroma_minus8 */
+ bitstream_put_ui(bs, 0, 1); /* qpprime_y_zero_transform_bypass_flag */
+ bitstream_put_ui(bs, 0, 1); /* seq_scaling_matrix_present_flag */
+ }
+
+ bitstream_put_ue(bs, seq_param->seq_fields.bits.log2_max_frame_num_minus4); /* log2_max_frame_num_minus4 */
+ bitstream_put_ue(bs, seq_param->seq_fields.bits.pic_order_cnt_type); /* pic_order_cnt_type */
+
+ if (seq_param->seq_fields.bits.pic_order_cnt_type == 0)
+ bitstream_put_ue(bs, seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4); /* log2_max_pic_order_cnt_lsb_minus4 */
+ else {
+ assert(0);
+ }
+
+ bitstream_put_ue(bs, seq_param->max_num_ref_frames); /* num_ref_frames */
+ bitstream_put_ui(bs, 0, 1); /* gaps_in_frame_num_value_allowed_flag */
+
+ bitstream_put_ue(bs, seq_param->picture_width_in_mbs - 1); /* pic_width_in_mbs_minus1 */
+ bitstream_put_ue(bs, seq_param->picture_height_in_mbs - 1); /* pic_height_in_map_units_minus1 */
+ bitstream_put_ui(bs, seq_param->seq_fields.bits.frame_mbs_only_flag, 1); /* frame_mbs_only_flag */
+
+ if (!seq_param->seq_fields.bits.frame_mbs_only_flag) {
+ assert(0);
+ }
+
+ bitstream_put_ui(bs, seq_param->seq_fields.bits.direct_8x8_inference_flag, 1); /* direct_8x8_inference_flag */
+ bitstream_put_ui(bs, seq_param->frame_cropping_flag, 1); /* frame_cropping_flag */
+
+ if (seq_param->frame_cropping_flag) {
+ bitstream_put_ue(bs, seq_param->frame_crop_left_offset); /* frame_crop_left_offset */
+ bitstream_put_ue(bs, seq_param->frame_crop_right_offset); /* frame_crop_right_offset */
+ bitstream_put_ue(bs, seq_param->frame_crop_top_offset); /* frame_crop_top_offset */
+ bitstream_put_ue(bs, seq_param->frame_crop_bottom_offset); /* frame_crop_bottom_offset */
+ }
+
+ if(ctx->frame_bitrate <= 0)
+ bitstream_put_ui(bs, 0, 1); /* vui_parameters_present_flag */
+ else {
+ bitstream_put_ui(bs, 1, 1); /* vui_parameters_present_flag */
+ bitstream_put_ui(bs, 0, 1); /* aspect_ratio_info_present_flag */
+ bitstream_put_ui(bs, 0, 1); /* overscan_info_present_flag */
+ bitstream_put_ui(bs, 0, 1); /* video_signal_type_present_flag */
+ bitstream_put_ui(bs, 0, 1); /* chroma_loc_info_present_flag */
+ bitstream_put_ui(bs, 1, 1); /* timing_info_present_flag */
+ {
+ bitstream_put_ui(bs, 15, 32);
+ bitstream_put_ui(bs, 900, 32);
+ bitstream_put_ui(bs, 1, 1);
+ }
+ bitstream_put_ui(bs, 1, 1); /* nal_hrd_parameters_present_flag */
+ {
+ // hrd_parameters
+ bitstream_put_ue(bs, 0); /* cpb_cnt_minus1 */
+ bitstream_put_ui(bs, 4, 4); /* bit_rate_scale */
+ bitstream_put_ui(bs, 6, 4); /* cpb_size_scale */
+
+ bitstream_put_ue(bs, ctx->frame_bitrate / 1024 - 1); /* bit_rate_value_minus1[0] */
+ bitstream_put_ue(bs, ctx->frame_bitrate / 1024 * 8 - 1); /* cpb_size_value_minus1[0] */
+ bitstream_put_ui(bs, 1, 1); /* cbr_flag[0] */
+
+ bitstream_put_ui(bs, 23, 5); /* initial_cpb_removal_delay_length_minus1 */
+ bitstream_put_ui(bs, 23, 5); /* cpb_removal_delay_length_minus1 */
+ bitstream_put_ui(bs, 23, 5); /* dpb_output_delay_length_minus1 */
+ bitstream_put_ui(bs, 23, 5); /* time_offset_length */
+ }
+ bitstream_put_ui(bs, 0, 1); /* vcl_hrd_parameters_present_flag */
+ bitstream_put_ui(bs, 0, 1); /* low_delay_hrd_flag */
+
+ bitstream_put_ui(bs, 0, 1); /* pic_struct_present_flag */
+ bitstream_put_ui(bs, 0, 1); /* bitstream_restriction_flag */
+ }
+
+ rbsp_trailing_bits(bs); /* rbsp_trailing_bits */
+}
+
+
+void ff_vaapi_h264enc_pps_rbsp(VAEncPictureParameterBufferH264 *pic_param, VaapiH264EncBitstream *bs)
+{
+ bitstream_put_ue(bs, pic_param->pic_parameter_set_id); /* pic_parameter_set_id */
+ bitstream_put_ue(bs, pic_param->seq_parameter_set_id); /* seq_parameter_set_id */
+
+ bitstream_put_ui(bs, pic_param->pic_fields.bits.entropy_coding_mode_flag, 1); /* entropy_coding_mode_flag */
+
+ bitstream_put_ui(bs, 0, 1); /* pic_order_present_flag: 0 */
+
+ bitstream_put_ue(bs, 0); /* num_slice_groups_minus1 */
+
+ bitstream_put_ue(bs, pic_param->num_ref_idx_l0_active_minus1); /* num_ref_idx_l0_active_minus1 */
+ bitstream_put_ue(bs, pic_param->num_ref_idx_l1_active_minus1); /* num_ref_idx_l1_active_minus1 1 */
+
+ bitstream_put_ui(bs, pic_param->pic_fields.bits.weighted_pred_flag, 1); /* weighted_pred_flag: 0 */
+ bitstream_put_ui(bs, pic_param->pic_fields.bits.weighted_bipred_idc, 2); /* weighted_bipred_idc: 0 */
+
+ bitstream_put_se(bs, pic_param->pic_init_qp - 26); /* pic_init_qp_minus26 */
+ bitstream_put_se(bs, 0); /* pic_init_qs_minus26 */
+ bitstream_put_se(bs, 0); /* chroma_qp_index_offset */
+
+ bitstream_put_ui(bs, pic_param->pic_fields.bits.deblocking_filter_control_present_flag, 1); /* deblocking_filter_control_present_flag */
+ bitstream_put_ui(bs, 0, 1); /* constrained_intra_pred_flag */
+ bitstream_put_ui(bs, 0, 1); /* redundant_pic_cnt_present_flag */
+
+ /* more_rbsp_data */
+ bitstream_put_ui(bs, pic_param->pic_fields.bits.transform_8x8_mode_flag, 1); /*transform_8x8_mode_flag */
+ bitstream_put_ui(bs, 0, 1); /* pic_scaling_matrix_present_flag */
+ bitstream_put_se(bs, pic_param->second_chroma_qp_index_offset ); /*second_chroma_qp_index_offset */
+
+ rbsp_trailing_bits(bs);
+}
+
+void ff_vaapi_h264enc_sei_rbsp(VaapiH264EncContext *ictx, VaapiH264EncBitstream *bs)
+{
+ unsigned char *byte_buf;
+ int bp_byte_size, i, pic_byte_size;
+ VaapiH264EncBitstream sei_bp_bs, sei_pic_bs;
+
+ ff_vaapi_h264enc_bs_start(&sei_bp_bs);
+ bitstream_put_ue(&sei_bp_bs, 0); /*seq_parameter_set_id*/
+ bitstream_put_ui(&sei_bp_bs, ictx->initial_cpb_removal_delay, ictx->initial_cpb_removal_delay_length);
+ bitstream_put_ui(&sei_bp_bs, 0, ictx->cpb_removal_delay_length);
+ if ( sei_bp_bs.bit_offset & 0x7) {
+ bitstream_put_ui(&sei_bp_bs, 1, 1);
+ }
+ ff_vaapi_h264enc_bs_end(&sei_bp_bs);
+ bp_byte_size = (sei_bp_bs.bit_offset + 7) / 8;
+
+ ff_vaapi_h264enc_bs_start(&sei_pic_bs);
+ bitstream_put_ui(&sei_pic_bs, ictx->cpb_removal_delay*ictx->current_frame_encoding, ictx->cpb_removal_delay_length);
+ bitstream_put_ui(&sei_pic_bs, 0, ictx->dpb_output_delay_length);
+ if ( sei_pic_bs.bit_offset & 0x7) {
+ bitstream_put_ui(&sei_pic_bs, 1, 1);
+ }
+ ff_vaapi_h264enc_bs_end(&sei_pic_bs);
+ pic_byte_size = (sei_pic_bs.bit_offset + 7) / 8;
+
+
+ /* Write the SEI buffer period data */
+ bitstream_put_ui(bs, 0, 8);
+ bitstream_put_ui(bs, bp_byte_size, 8);
+
+ byte_buf = (unsigned char *)sei_bp_bs.buffer;
+ for(i = 0; i < bp_byte_size; i++) {
+ bitstream_put_ui(bs, byte_buf[i], 8);
+ }
+ free(byte_buf);
+
+ /* write the SEI timing data */
+ bitstream_put_ui(bs, 0x01, 8);
+ bitstream_put_ui(bs, pic_byte_size, 8);
+
+ byte_buf = (unsigned char *)sei_pic_bs.buffer;
+ for(i = 0; i < pic_byte_size; i++) {
+ bitstream_put_ui(bs, byte_buf[i], 8);
+ }
+ free(byte_buf);
+
+ rbsp_trailing_bits(bs);
+}
+
+void ff_vaapi_h264enc_slice_header(VaapiH264EncContext *ictx, VaapiH264EncBitstream *bs)
+{
+ int first_mb_in_slice = ictx->slice_param.macroblock_address;
+
+ bitstream_put_ue(bs, first_mb_in_slice); /* first_mb_in_slice: 0 */
+ bitstream_put_ue(bs, ictx->slice_param.slice_type); /* slice_type */
+ bitstream_put_ue(bs, ictx->slice_param.pic_parameter_set_id); /* pic_parameter_set_id: 0 */
+ bitstream_put_ui(bs,
+ ictx->pic_param.frame_num,
+ ictx->seq_param.seq_fields.bits.log2_max_frame_num_minus4 + 4); /* frame_num */
+
+ /* frame_mbs_only_flag == 1 */
+ if (!ictx->seq_param.seq_fields.bits.frame_mbs_only_flag) {
+ /* FIXME: */
+ assert(0);
+ }
+
+ if (ictx->pic_param.pic_fields.bits.idr_pic_flag)
+ bitstream_put_ue(bs, ictx->slice_param.idr_pic_id); /* idr_pic_id: 0 */
+
+ if (ictx->seq_param.seq_fields.bits.pic_order_cnt_type == 0) {
+ bitstream_put_ui(bs, ictx->pic_param.CurrPic.TopFieldOrderCnt, ictx->seq_param.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 + 4);
+ /* pic_order_present_flag == 0 */
+ } else {
+ /* FIXME: */
+ assert(0);
+ }
+
+ /* redundant_pic_cnt_present_flag == 0 */
+ /* slice type */
+ if (IS_P_SLICE(ictx->slice_param.slice_type)) {
+ bitstream_put_ui(bs, ictx->slice_param.num_ref_idx_active_override_flag, 1); /* num_ref_idx_active_override_flag: */
+
+ if (ictx->slice_param.num_ref_idx_active_override_flag)
+ bitstream_put_ue(bs, ictx->slice_param.num_ref_idx_l0_active_minus1);
+
+ /* ref_pic_list_reordering */
+ bitstream_put_ui(bs, 0, 1); /* ref_pic_list_reordering_flag_l0: 0 */
+ } else if (IS_B_SLICE(ictx->slice_param.slice_type)) {
+ bitstream_put_ui(bs, ictx->slice_param.direct_spatial_mv_pred_flag, 1); /* direct_spatial_mv_pred: 1 */
+
+ bitstream_put_ui(bs, ictx->slice_param.num_ref_idx_active_override_flag, 1); /* num_ref_idx_active_override_flag: */
+
+ if (ictx->slice_param.num_ref_idx_active_override_flag) {
+ bitstream_put_ue(bs, ictx->slice_param.num_ref_idx_l0_active_minus1);
+ bitstream_put_ue(bs, ictx->slice_param.num_ref_idx_l1_active_minus1);
+ }
+
+ /* ref_pic_list_reordering */
+ bitstream_put_ui(bs, 0, 1); /* ref_pic_list_reordering_flag_l0: 0 */
+ bitstream_put_ui(bs, 0, 1); /* ref_pic_list_reordering_flag_l1: 0 */
+ }
+
+ if ((ictx->pic_param.pic_fields.bits.weighted_pred_flag &&
+ IS_P_SLICE(ictx->slice_param.slice_type)) ||
+ ((ictx->pic_param.pic_fields.bits.weighted_bipred_idc == 1) &&
+ IS_B_SLICE(ictx->slice_param.slice_type))) {
+ /* FIXME: fill weight/offset table */
+ assert(0);
+ }
+
+ /* dec_ref_pic_marking */
+ if (ictx->pic_param.pic_fields.bits.reference_pic_flag) { /* nal_ref_idc != 0 */
+ unsigned char no_output_of_prior_pics_flag = 0;
+ unsigned char long_term_reference_flag = 0;
+ unsigned char adaptive_ref_pic_marking_mode_flag = 0;
+
+ if (ictx->pic_param.pic_fields.bits.idr_pic_flag) {
+ bitstream_put_ui(bs, no_output_of_prior_pics_flag, 1); /* no_output_of_prior_pics_flag: 0 */
+ bitstream_put_ui(bs, long_term_reference_flag, 1); /* long_term_reference_flag: 0 */
+ } else {
+ bitstream_put_ui(bs, adaptive_ref_pic_marking_mode_flag, 1); /* adaptive_ref_pic_marking_mode_flag: 0 */
+ }
+ }
+
+ if (ictx->pic_param.pic_fields.bits.entropy_coding_mode_flag &&
+ !IS_I_SLICE(ictx->slice_param.slice_type))
+ bitstream_put_ue(bs, ictx->slice_param.cabac_init_idc); /* cabac_init_idc: 0 */
+
+ bitstream_put_se(bs, ictx->slice_param.slice_qp_delta); /* slice_qp_delta: 0 */
+
+ /* ignore for SP/SI */
+
+ if (ictx->pic_param.pic_fields.bits.deblocking_filter_control_present_flag) {
+ bitstream_put_ue(bs, ictx->slice_param.disable_deblocking_filter_idc); /* disable_deblocking_filter_idc: 0 */
+
+ if (ictx->slice_param.disable_deblocking_filter_idc != 1) {
+ bitstream_put_se(bs, ictx->slice_param.slice_alpha_c0_offset_div2); /* slice_alpha_c0_offset_div2: 2 */
+ bitstream_put_se(bs, ictx->slice_param.slice_beta_offset_div2); /* slice_beta_offset_div2: 2 */
+ }
+ }
+
+ if (ictx->pic_param.pic_fields.bits.entropy_coding_mode_flag) {
+ bitstream_byte_aligning(bs, 1);
+ }
+}
diff --git a/libavcodec/vaapi_h264enc_paramset.h b/libavcodec/vaapi_h264enc_paramset.h
new file mode 100644
index 0000000..12ea248
--- /dev/null
+++ b/libavcodec/vaapi_h264enc_paramset.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 Bryan Christ
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VAAPI_H264ENC_PARAMSET_H
+#define AVCODEC_VAAPI_H264ENC_PARAMSET_H
+
+#include <va/va.h>
+#include <va/va_enc_h264.h>
+
+#include "vaapi_h264enc.h"
+
+
+#define NAL_REF_IDC_NONE 0
+#define NAL_REF_IDC_LOW 1
+#define NAL_REF_IDC_MEDIUM 2
+#define NAL_REF_IDC_HIGH 3
+
+#define NAL_NON_IDR 1
+#define NAL_IDR 5
+#define NAL_SPS 7
+#define NAL_PPS 8
+#define NAL_SEI 6
+
+#define SLICE_TYPE_P 0
+#define SLICE_TYPE_B 1
+#define SLICE_TYPE_I 2
+#define IS_P_SLICE(type) (SLICE_TYPE_P == (type))
+#define IS_B_SLICE(type) (SLICE_TYPE_B == (type))
+#define IS_I_SLICE(type) (SLICE_TYPE_I == (type))
+
+#define ENTROPY_MODE_CAVLC 0
+#define ENTROPY_MODE_CABAC 1
+
+#define PROFILE_IDC_BASELINE 66
+#define PROFILE_IDC_MAIN 77
+#define PROFILE_IDC_HIGH 100
+
+#define BITSTREAM_ALLOCATE_STEPPING 4096
+
+
+typedef struct VaapiH264EncBitstream {
+ unsigned int *buffer;
+ int bit_offset;
+ int max_size_in_dword;
+} VaapiH264EncBitstream;
+
+
+void ff_vaapi_h264enc_bs_start(VaapiH264EncBitstream *bs);
+
+void ff_vaapi_h264enc_bs_end(VaapiH264EncBitstream *bs);
+
+void ff_vaapi_h264enc_nal_start_code_prefix(VaapiH264EncBitstream *bs);
+
+void ff_vaapi_h264enc_nal_header(VaapiH264EncBitstream *bs, int nal_ref_idc, int nal_unit_type);
+
+void ff_vaapi_h264enc_sps_rbsp(VaapiH264EncContext *ctx, VAEncSequenceParameterBufferH264 *seq_param, VaapiH264EncBitstream *bs);
+
+void ff_vaapi_h264enc_pps_rbsp(VAEncPictureParameterBufferH264 *pic_param, VaapiH264EncBitstream *bs);
+
+void ff_vaapi_h264enc_sei_rbsp(VaapiH264EncContext *ictx, VaapiH264EncBitstream *bs);
+
+void ff_vaapi_h264enc_slice_header(VaapiH264EncContext *ictx, VaapiH264EncBitstream *bs);
+
+#endif /* AVCODEC_VAAPI_H264ENC_PARAMSET_H */
diff --git a/libavcodec/version.h b/libavcodec/version.h
index b17b794..9846275 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
#include "libavutil/version.h"
#define LIBAVCODEC_VERSION_MAJOR 57
-#define LIBAVCODEC_VERSION_MINOR 20
+#define LIBAVCODEC_VERSION_MINOR 21
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
--
1.9.1
More information about the ffmpeg-devel
mailing list