From be5f142f820e05b411bfdc95719e0ab6388bcbe5 Mon Sep 17 00:00:00 2001
From: Amancio Hasty <ahasty@gmail.com>
Date: Fri, 18 Mar 2016 10:00:14 -0700
Subject: [PATCH] added support for hardware assist H264  video encoding for
 the Raspberry Pi

---
 configure              |   4 +
 libavcodec/Makefile    |   1 +
 libavcodec/allcodecs.c |   1 +
 libavcodec/vc264.c     | 387 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 393 insertions(+)
 create mode 100644 libavcodec/vc264.c

diff --git a/configure b/configure
index e5de306..e4af0eb 100755
--- a/configure
+++ b/configure
@@ -6286,6 +6286,10 @@ for type in decoder encoder hwaccel parser demuxer muxer protocol filter bsf ind
     print_enabled '_*' $list | print_in_columns
     echo
 done
+if enabled vc264_encoder &&  [ -n "$target_os_default"="linux" ] ; then
+        add_cflags ' -I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/src/hello_pi/libs/ilclient/'
+        prepend LDFLAGS ' -L/opt/vc/lib -lilclient  -lGLESv2 -lEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lrt'
+fi
 
 license="LGPL version 2.1 or later"
 if enabled nonfree; then
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 6bb1af1..09a1956 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -817,6 +817,7 @@ OBJS-$(CONFIG_LIBMP3LAME_ENCODER)         += libmp3lame.o mpegaudiodecheader.o
 OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER)  += libopencore-amr.o
 OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER)  += libopencore-amr.o
 OBJS-$(CONFIG_LIBOPENCORE_AMRWB_DECODER)  += libopencore-amr.o
+OBJS-$(CONFIG_VC264_ENCODER)              += vc264.o
 OBJS-$(CONFIG_LIBOPENH264_ENCODER)        += libopenh264enc.o
 OBJS-$(CONFIG_LIBOPENJPEG_DECODER)        += libopenjpegdec.o
 OBJS-$(CONFIG_LIBOPENJPEG_ENCODER)        += libopenjpegenc.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 2a25d66..f68f2bf 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -589,6 +589,7 @@ void avcodec_register_all(void)
     REGISTER_ENCODER(LIBWEBP_ANIM,      libwebp_anim);  /* preferred over libwebp */
     REGISTER_ENCODER(LIBWEBP,           libwebp);
     REGISTER_ENCODER(LIBX262,           libx262);
+    REGISTER_ENCODER(VC264,             vc264);
     REGISTER_ENCODER(LIBX264,           libx264);
     REGISTER_ENCODER(LIBX264RGB,        libx264rgb);
     REGISTER_ENCODER(LIBX265,           libx265);
diff --git a/libavcodec/vc264.c b/libavcodec/vc264.c
new file mode 100644
index 0000000..fcccd5c
--- /dev/null
+++ b/libavcodec/vc264.c
@@ -0,0 +1,387 @@
+/*  H.264 hardware assist video encoding code taken from
+ * raspberry's os :
+ *   /opt/vc/src/hello_pi/hello_encode/encode.c
+ */
+
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+Copyright (c) 2012, Kalle Vahlman <zuh@iki>
+                    Tuomas Kulve <tuomas@kulve.fi>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+      * Neither the name of the copyright holder nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * ffmpeg driver for hardware assist video H.264 encoding using Broadcom's GPU 
+ * Copyright (C) 2016 Amancio Hasty ahasty@gmail.com
+ *
+ *
+ * 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
+ * 
+ */
+
+
+/**
+ * @ file vc264.c
+ * Broadcom bm2865's Visual Core hardware assist h264 using
+   openMax interface to the GPU.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define OMX_SKIP64BIT 
+#include "bcm_host.h"
+#include "ilclient.h"
+#include "avcodec.h"
+#include "internal.h"
+
+typedef struct VC264Context {
+  OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+  OMX_PARAM_PORTDEFINITIONTYPE def;
+  COMPONENT_T *video_encode;
+  COMPONENT_T *list[5];
+  OMX_BUFFERHEADERTYPE *buf;
+  OMX_BUFFERHEADERTYPE *out;
+  ILCLIENT_T *client;
+  OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
+  int  width;
+  int height;
+  int bit_rate;
+} VC264Context;
+
+
+static int vc264_init(AVCodecContext *avctx) {
+
+
+
+   OMX_ERRORTYPE r;
+   int error;
+
+
+
+   VC264Context *vc = avctx->priv_data;
+
+   vc->width = avctx->width;
+   vc->height = avctx->height;
+   vc->bit_rate = avctx->bit_rate;
+
+#if FF_API_CODED_FRAME
+FF_DISABLE_DEPRECATION_WARNINGS
+
+   avctx->coded_frame = av_frame_alloc();
+   avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+
+   memset(&vc->list, 0, sizeof(vc->list));
+   bcm_host_init();
+   if ((vc->client = ilclient_init()) == NULL) {
+      return -3;
+   }
+   error = OMX_Init();   
+
+   if (error  != OMX_ErrorNone) {
+      ilclient_destroy(vc->client);
+      av_log(avctx,AV_LOG_ERROR,"in vc264_init OMX_Init failed ");
+     return -4;
+    }
+
+   // create video_encode
+   r = ilclient_create_component(vc->client, &vc->video_encode, (char *) "video_encode",
+				 ILCLIENT_DISABLE_ALL_PORTS |
+				 ILCLIENT_ENABLE_INPUT_BUFFERS |
+				 ILCLIENT_ENABLE_OUTPUT_BUFFERS);
+
+   if (r != 0) {
+      
+	av_log(avctx,AV_LOG_ERROR,"ilclient_create_component() for video_encode failed with %x!",
+	  r);
+      exit(1);
+   }
+   vc->list[0] = vc->video_encode;
+
+ // get current settings of video_encode component from port 200
+   memset(&vc->def, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+   vc->def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
+   vc->def.nVersion.nVersion = OMX_VERSION;
+   vc->def.nPortIndex = 200;
+
+
+
+
+
+
+
+   if (OMX_GetParameter
+       (ILC_GET_HANDLE(vc->video_encode), OMX_IndexParamPortDefinition,
+	&vc->def) != OMX_ErrorNone) {
+      av_log(avctx,AV_LOG_ERROR,"%s:%d: OMX_GetParameter() for video_encode port 200 failed!",
+	     __FUNCTION__, __LINE__);
+      exit(1);
+   }
+
+
+
+
+ // Port 200: in 1/1 115200 16 enabled,not pop.,not cont. 320x240 320x240 @1966080 20
+   vc->def.format.video.nFrameWidth = vc->width;
+   vc->def.format.video.nFrameHeight = vc->height;
+   vc->def.format.video.xFramerate = avctx->time_base.den << 16;
+   vc->def.format.video.nSliceHeight = vc->def.format.video.nFrameHeight;
+   vc->def.format.video.nStride = vc->def.format.video.nFrameWidth;
+   vc->def.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
+
+
+   r = OMX_SetParameter(ILC_GET_HANDLE(vc->video_encode),
+			OMX_IndexParamPortDefinition, &vc->def);
+   if (r != OMX_ErrorNone) {
+      av_log(avctx,AV_LOG_ERROR,"%s:%d: OMX_SetParameter() for video_encode port 200 failed with %x!",
+	  __FUNCTION__, __LINE__, r);
+      exit(1);
+   }
+
+   memset(&vc->format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE));
+   vc->format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE);
+   vc->format.nVersion.nVersion = OMX_VERSION;
+   vc->format.nPortIndex = 201;
+   vc->format.eCompressionFormat = OMX_VIDEO_CodingAVC;
+
+   r = OMX_SetParameter(ILC_GET_HANDLE(vc->video_encode),
+			OMX_IndexParamVideoPortFormat, &vc->format);
+   if (r != OMX_ErrorNone) {
+      
+	av_log(avctx,AV_LOG_ERROR,"%s:%d: OMX_SetParameter() for video_encode port 201 failed with %x!",
+	  __FUNCTION__, __LINE__, r);
+      exit(1);
+   }
+
+
+   memset(&vc->bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE));
+   vc->bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE);
+   vc->bitrateType.nVersion.nVersion = OMX_VERSION;
+   vc->bitrateType.eControlRate = OMX_Video_ControlRateVariable;
+
+
+   vc->bitrateType.nTargetBitrate = avctx->bit_rate ;
+   vc->bitrateType.nPortIndex = 201;
+   r = OMX_SetParameter(ILC_GET_HANDLE(vc->video_encode),
+                       OMX_IndexParamVideoBitrate, &vc->bitrateType);
+   if (r != OMX_ErrorNone) {
+
+        av_log(avctx,AV_LOG_ERROR,"%s:%d: OMX_SetParameter() for bitrate for video_encode port 201 failed with %x!",
+         __FUNCTION__, __LINE__, r);
+      exit(1);
+   }
+
+
+   // get current bitrate
+   memset(&vc->bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE));
+   vc->bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE);
+   vc->bitrateType.nVersion.nVersion = OMX_VERSION;
+   vc->bitrateType.nPortIndex = 201;
+
+   if (OMX_GetParameter
+       (ILC_GET_HANDLE(vc->video_encode), OMX_IndexParamVideoBitrate,
+       &vc->bitrateType) != OMX_ErrorNone) {
+      av_log(avctx,AV_LOG_ERROR,"%s:%d: OMX_GetParameter() for video_encode for bitrate port 201 failed!",
+            __FUNCTION__, __LINE__);
+      exit(1);
+   }
+
+
+
+
+
+   if (ilclient_change_component_state(vc->video_encode, OMX_StateIdle) == -1) {
+
+	 av_log(avctx,AV_LOG_ERROR,"%s:%d: ilclient_change_component_state(vc->video_encode, OMX_StateIdle) failed",
+	  __FUNCTION__, __LINE__);
+   }
+
+
+   if (ilclient_enable_port_buffers(vc->video_encode, 200, NULL, NULL, NULL) != 0) {
+      av_log(avctx,AV_LOG_ERROR,"enabling port buffers for 200 failed!\n");
+      exit(1);
+   }
+
+   if (ilclient_enable_port_buffers(vc->video_encode, 201, NULL, NULL, NULL) != 0) {
+      av_log(avctx,AV_LOG_ERROR,"enabling port buffers for 201 failed!\n");
+      exit(1);
+   }
+
+
+   ilclient_change_component_state(vc->video_encode, OMX_StateExecuting);
+
+   return 0;
+}
+
+static int vc264_encode (AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame,
+	       int *got_packet_ptr) {
+
+  VC264Context *vc = avctx->priv_data;
+  OMX_ERRORTYPE r;
+  OMX_BUFFERHEADERTYPE *buf;
+  OMX_BUFFERHEADERTYPE *out;
+  char *dst;
+  char *in_buf;
+  int ret;
+  int height16;
+  int b_size;
+  height16 = vc->height+15&~15;
+  b_size = (vc->width * height16 * 3)/2;
+
+
+
+
+  buf = ilclient_get_input_buffer(vc->video_encode, 200, 1);
+  if (buf == NULL) {
+    av_log(avctx,AV_LOG_ERROR," vc264_encode: ran out gpu buffers");
+  }
+  else {
+    /*********** fill buffer ************/
+    
+         if (frame->opaque == NULL ) {
+	   in_buf = frame->data[0];
+	 } else {
+	   in_buf = frame->opaque;
+	 }
+
+	 memcpy(buf->pBuffer, in_buf, b_size);
+	   
+	 buf->nFilledLen = b_size;
+
+
+	 if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(vc->video_encode), buf) !=
+	     OMX_ErrorNone) {
+	   av_log(avctx,AV_LOG_ERROR,"vc264_encode: Error emptying buffer!\n");
+	 }
+
+	 out = ilclient_get_output_buffer(vc->video_encode, 201, 1);
+
+	 r = OMX_FillThisBuffer(ILC_GET_HANDLE(vc->video_encode), out);
+	 if (r != OMX_ErrorNone) {
+	    av_log(avctx,AV_LOG_ERROR,"vc264_encode: Error filling buffer: %x\n", r);
+	 }
+
+
+	 if ( (ret = ff_alloc_packet2(avctx, avpkt, out->nFilledLen,0) ) )
+	   return ret;
+	 dst = avpkt->data;
+	 *got_packet_ptr = 1;
+
+	 /* out->pBuffer has buffer of size out->nFilledLen */
+
+	 memcpy(dst, out->pBuffer, out->nFilledLen);
+	 out->nFilledLen = 0;	 
+	 return 0;
+  }
+  return 0;
+}
+
+static int vc264_close(AVCodecContext *avctx)
+{
+  VC264Context *vc = avctx->priv_data;
+  ilclient_disable_port_buffers(vc->video_encode, 200, NULL, NULL, NULL);
+   ilclient_disable_port_buffers(vc->video_encode, 201, NULL, NULL, NULL);
+
+   ilclient_state_transition(vc->list, OMX_StateIdle);
+   ilclient_state_transition(vc->list, OMX_StateLoaded);
+
+   ilclient_cleanup_components(vc->list);
+
+   OMX_Deinit();
+   
+   ilclient_destroy(vc->client);
+
+#if FF_API_CODED_FRAME
+FF_DISABLE_DEPRECATION_WARNINGS
+   av_frame_free(&avctx->coded_frame);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+   return 0;
+}
+
+
+static const AVCodecDefault x264_defaults[] = {
+    { "b",                "0" },
+    { "bf",               "-1" },
+    { "flags2",           "0" },
+    { "g",                "-1" },
+    { "i_qfactor",        "-1" },
+    { "b_qfactor",        "-1" },
+    { "qmin",             "-1" },
+    { "qmax",             "-1" },
+    { "qdiff",            "-1" },
+    { "qblur",            "-1" },
+    { "qcomp",            "-1" },
+//     { "rc_lookahead",     "-1" },
+    { "refs",             "-1" },
+    { "sc_threshold",     "-1" },
+    { "trellis",          "-1" },
+    { "nr",               "-1" },
+    { "me_range",         "-1" },
+    { "me_method",        "-1" },
+    { "subq",             "-1" },
+    { "b_strategy",       "-1" },
+    { "keyint_min",       "-1" },
+    { "coder",            "-1" },
+    { "cmp",              "-1" },
+    { NULL },
+};
+
+AVCodec ff_vc264_encoder = {
+    .name           = "vc264",
+    .long_name      = NULL_IF_CONFIG_SMALL("Video Core H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_H264,
+    .priv_data_size = sizeof(VC264Context),
+    .init           = vc264_init,
+    .encode2        = vc264_encode,
+    .close          = vc264_close,
+    .capabilities   = 0,
+    .defaults         = x264_defaults,
+    .pix_fmts       = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P,
+									AV_PIX_FMT_NONE },
+
+};
+
+
+
-- 
2.1.4

