omx: Add support for zerocopy input of frames
[ffmpeg.git] / libavcodec / omx.c
index 98277d9..01920cb 100644 (file)
@@ -224,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,
@@ -303,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;
@@ -525,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++)
@@ -571,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++) {
@@ -611,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;
@@ -706,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
@@ -808,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 }
 };