omx: Add support for zerocopy input of frames
[ffmpeg.git] / libavcodec / omx.c
index 6a4a5a1..01920cb 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "config.h"
+
+#if CONFIG_OMX_RPI
+#define OMX_SKIP64BIT
+#endif
+
 #include <dlfcn.h>
 #include <OMX_Core.h>
 #include <OMX_Component.h>
@@ -69,6 +75,7 @@ static int64_t from_omx_ticks(OMX_TICKS value)
 
 typedef struct OMXContext {
     void *lib;
+    void *lib2;
     OMX_ERRORTYPE (*ptr_Init)(void);
     OMX_ERRORTYPE (*ptr_Deinit)(void);
     OMX_ERRORTYPE (*ptr_ComponentNameEnum)(OMX_STRING, OMX_U32, OMX_U32);
@@ -76,6 +83,7 @@ typedef struct OMXContext {
     OMX_ERRORTYPE (*ptr_FreeHandle)(OMX_HANDLETYPE);
     OMX_ERRORTYPE (*ptr_GetComponentsOfRole)(OMX_STRING, OMX_U32*, OMX_U8**);
     OMX_ERRORTYPE (*ptr_GetRolesOfComponent)(OMX_STRING, OMX_U32*, OMX_U8**);
+    void (*host_init)(void);
 } OMXContext;
 
 static av_cold void *dlsym_prefixed(void *handle, const char *symbol, const char *prefix)
@@ -86,8 +94,23 @@ static av_cold void *dlsym_prefixed(void *handle, const char *symbol, const char
 }
 
 static av_cold int omx_try_load(OMXContext *s, void *logctx,
-                                const char *libname, const char *prefix)
+                                const char *libname, const char *prefix,
+                                const char *libname2)
 {
+    if (libname2) {
+        s->lib2 = dlopen(libname2, RTLD_NOW | RTLD_GLOBAL);
+        if (!s->lib2) {
+            av_log(logctx, AV_LOG_WARNING, "%s not found\n", libname);
+            return AVERROR_ENCODER_NOT_FOUND;
+        }
+        s->host_init = dlsym(s->lib2, "bcm_host_init");
+        if (!s->host_init) {
+            av_log(logctx, AV_LOG_WARNING, "bcm_host_init not found\n");
+            dlclose(s->lib2);
+            s->lib2 = NULL;
+            return AVERROR_ENCODER_NOT_FOUND;
+        }
+    }
     s->lib = dlopen(libname, RTLD_NOW | RTLD_GLOBAL);
     if (!s->lib) {
         av_log(logctx, AV_LOG_WARNING, "%s not found\n", libname);
@@ -106,6 +129,9 @@ static av_cold int omx_try_load(OMXContext *s, void *logctx,
         av_log(logctx, AV_LOG_WARNING, "Not all functions found in %s\n", libname);
         dlclose(s->lib);
         s->lib = NULL;
+        if (s->lib2)
+            dlclose(s->lib2);
+        s->lib2 = NULL;
         return AVERROR_ENCODER_NOT_FOUND;
     }
     return 0;
@@ -114,8 +140,12 @@ static av_cold int omx_try_load(OMXContext *s, void *logctx,
 static av_cold OMXContext *omx_init(void *logctx, const char *libname, const char *prefix)
 {
     static const char * const libnames[] = {
-        "libOMX_Core.so",
-        "libOmxCore.so",
+#if CONFIG_OMX_RPI
+        "/opt/vc/lib/libopenmaxil.so", "/opt/vc/lib/libbcm_host.so",
+#else
+        "libOMX_Core.so", NULL,
+        "libOmxCore.so", NULL,
+#endif
         NULL
     };
     const char* const* nameptr;
@@ -126,14 +156,14 @@ static av_cold OMXContext *omx_init(void *logctx, const char *libname, const cha
     if (!omx_context)
         return NULL;
     if (libname) {
-        ret = omx_try_load(omx_context, logctx, libname, prefix);
+        ret = omx_try_load(omx_context, logctx, libname, prefix, NULL);
         if (ret < 0) {
             av_free(omx_context);
             return NULL;
         }
     } else {
-        for (nameptr = libnames; *nameptr; nameptr++)
-            if (!(ret = omx_try_load(omx_context, logctx, *nameptr, prefix)))
+        for (nameptr = libnames; *nameptr; nameptr += 2)
+            if (!(ret = omx_try_load(omx_context, logctx, nameptr[0], prefix, nameptr[1])))
                 break;
         if (!*nameptr) {
             av_free(omx_context);
@@ -141,6 +171,8 @@ static av_cold OMXContext *omx_init(void *logctx, const char *libname, const cha
         }
     }
 
+    if (omx_context->host_init)
+        omx_context->host_init();
     omx_context->ptr_Init();
     return omx_context;
 }
@@ -192,6 +224,8 @@ typedef struct OMXCodecContext {
 
     uint8_t *output_buf;
     int output_buf_size;
+
+    int input_zerocopy;
 } OMXCodecContext;
 
 static void append_buffer(pthread_mutex_t *mutex, pthread_cond_t *cond,
@@ -271,6 +305,15 @@ static OMX_ERRORTYPE empty_buffer_done(OMX_HANDLETYPE component, OMX_PTR app_dat
                                        OMX_BUFFERHEADERTYPE *buffer)
 {
     OMXCodecContext *s = app_data;
+    if (s->input_zerocopy) {
+        if (buffer->pAppPrivate) {
+            if (buffer->pOutputPortPrivate)
+                av_free(buffer->pAppPrivate);
+            else
+                av_frame_free((AVFrame**)&buffer->pAppPrivate);
+            buffer->pAppPrivate = NULL;
+        }
+    }
     append_buffer(&s->input_mutex, &s->input_cond,
                   &s->num_free_in_buffers, s->free_in_buffers, buffer);
     return OMX_ErrorNone;
@@ -298,6 +341,12 @@ static av_cold int find_component(OMXContext *omx_context, void *logctx,
     char **components;
     int ret = 0;
 
+#if CONFIG_OMX_RPI
+    if (av_strstart(role, "video_encoder.", NULL)) {
+        av_strlcpy(str, "OMX.broadcom.video_encode", str_size);
+        return 0;
+    }
+#endif
     omx_context->ptr_GetComponentsOfRole((OMX_STRING) role, &num, NULL);
     if (!num) {
         av_log(logctx, AV_LOG_WARNING, "No component for role %s found\n", role);
@@ -487,8 +536,14 @@ static av_cold int omx_component_init(AVCodecContext *avctx, const char *role)
     s->done_out_buffers   = av_mallocz(sizeof(OMX_BUFFERHEADERTYPE*) * s->num_out_buffers);
     if (!s->in_buffer_headers || !s->free_in_buffers || !s->out_buffer_headers || !s->done_out_buffers)
         return AVERROR(ENOMEM);
-    for (i = 0; i < s->num_in_buffers && err == OMX_ErrorNone; i++)
-        err = OMX_AllocateBuffer(s->handle, &s->in_buffer_headers[i],  s->in_port,  s, in_port_params.nBufferSize);
+    for (i = 0; i < s->num_in_buffers && err == OMX_ErrorNone; i++) {
+        if (s->input_zerocopy)
+            err = OMX_UseBuffer(s->handle, &s->in_buffer_headers[i], s->in_port, s, in_port_params.nBufferSize, NULL);
+        else
+            err = OMX_AllocateBuffer(s->handle, &s->in_buffer_headers[i],  s->in_port,  s, in_port_params.nBufferSize);
+        if (err == OMX_ErrorNone)
+            s->in_buffer_headers[i]->pAppPrivate = s->in_buffer_headers[i]->pOutputPortPrivate = NULL;
+    }
     CHECK(err);
     s->num_in_buffers = i;
     for (i = 0; i < s->num_out_buffers && err == OMX_ErrorNone; i++)
@@ -533,6 +588,8 @@ static av_cold void cleanup(OMXCodecContext *s)
         for (i = 0; i < s->num_in_buffers; i++) {
             OMX_BUFFERHEADERTYPE *buffer = get_buffer(&s->input_mutex, &s->input_cond,
                                                       &s->num_free_in_buffers, s->free_in_buffers, 1);
+            if (s->input_zerocopy)
+                buffer->pBuffer = NULL;
             OMX_FreeBuffer(s->handle, s->in_port, buffer);
         }
         for (i = 0; i < s->num_out_buffers; i++) {
@@ -573,6 +630,10 @@ static av_cold int omx_encode_init(AVCodecContext *avctx)
     OMX_BUFFERHEADERTYPE *buffer;
     OMX_ERRORTYPE err;
 
+#if CONFIG_OMX_RPI
+    s->input_zerocopy = 1;
+#endif
+
     s->omx_context = omx_init(avctx, s->libname, s->libprefix);
     if (!s->omx_context)
         return AVERROR_ENCODER_NOT_FOUND;
@@ -668,11 +729,57 @@ static int omx_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     if (frame) {
         uint8_t *dst[4];
         int linesize[4];
+        int need_copy;
         buffer = get_buffer(&s->input_mutex, &s->input_cond,
                             &s->num_free_in_buffers, s->free_in_buffers, 1);
 
         buffer->nFilledLen = av_image_fill_arrays(dst, linesize, buffer->pBuffer, avctx->pix_fmt, s->stride, s->plane_size, 1);
-        av_image_copy(dst, linesize, (const uint8_t**) frame->data, frame->linesize, avctx->pix_fmt, avctx->width, avctx->height);
+
+        if (s->input_zerocopy) {
+            uint8_t *src[4] = { NULL };
+            int src_linesize[4];
+            av_image_fill_arrays(src, src_linesize, frame->data[0], avctx->pix_fmt, s->stride, s->plane_size, 1);
+            if (frame->linesize[0] == src_linesize[0] &&
+                frame->linesize[1] == src_linesize[1] &&
+                frame->linesize[2] == src_linesize[2] &&
+                frame->data[1] == src[1] &&
+                frame->data[2] == src[2]) {
+                // If the input frame happens to have all planes stored contiguously,
+                // with the right strides, just clone the frame and set the OMX
+                // buffer header to point to it
+                AVFrame *local = av_frame_clone(frame);
+                if (!local) {
+                    // Return the buffer to the queue so it's not lost
+                    append_buffer(&s->input_mutex, &s->input_cond, &s->num_free_in_buffers, s->free_in_buffers, buffer);
+                    return AVERROR(ENOMEM);
+                } else {
+                    buffer->pAppPrivate = local;
+                    buffer->pOutputPortPrivate = NULL;
+                    buffer->pBuffer = local->data[0];
+                    need_copy = 0;
+                }
+            } else {
+                // If not, we need to allocate a new buffer with the right
+                // size and copy the input frame into it.
+                uint8_t *buf = av_malloc(av_image_get_buffer_size(avctx->pix_fmt, s->stride, s->plane_size, 1));
+                if (!buf) {
+                    // Return the buffer to the queue so it's not lost
+                    append_buffer(&s->input_mutex, &s->input_cond, &s->num_free_in_buffers, s->free_in_buffers, buffer);
+                    return AVERROR(ENOMEM);
+                } else {
+                    buffer->pAppPrivate = buf;
+                    // Mark that pAppPrivate is an av_malloc'ed buffer, not an AVFrame
+                    buffer->pOutputPortPrivate = (void*) 1;
+                    buffer->pBuffer = buf;
+                    need_copy = 1;
+                    buffer->nFilledLen = av_image_fill_arrays(dst, linesize, buffer->pBuffer, avctx->pix_fmt, s->stride, s->plane_size, 1);
+                }
+            }
+        } else {
+            need_copy = 1;
+        }
+        if (need_copy)
+            av_image_copy(dst, linesize, (const uint8_t**) frame->data, frame->linesize, avctx->pix_fmt, avctx->width, avctx->height);
         buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
         buffer->nOffset = 0;
         // Convert the timestamps to microseconds; some encoders can ignore
@@ -770,9 +877,11 @@ static av_cold int omx_encode_end(AVCodecContext *avctx)
 
 #define OFFSET(x) offsetof(OMXCodecContext, x)
 #define VDE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+#define VE  AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
     { "omx_libname", "OpenMAX library name", OFFSET(libname), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VDE },
     { "omx_libprefix", "OpenMAX library prefix", OFFSET(libprefix), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VDE },
+    { "zerocopy", "Try to avoid copying input frames if possible", OFFSET(input_zerocopy), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE },
     { NULL }
 };