---
 doc/multithreading.txt |    5 ++
 libavcodec/internal.h  |   15 +++++
 libavcodec/pthread.c   |  169 ++++++++++++++++++++++++------------------------
 libavcodec/thread.h    |   20 ++++--
 libavcodec/utils.c     |   19 ++++++
 5 files changed, 139 insertions(+), 89 deletions(-)

diff --git a/doc/multithreading.txt b/doc/multithreading.txt
index b72bc16..9b27b10 100644
--- a/doc/multithreading.txt
+++ b/doc/multithreading.txt
@@ -57,6 +57,11 @@ which re-allocates them for other threads.
 Add CODEC_CAP_FRAME_THREADS to the codec capabilities. There will be very 
little
 speed gain at this point but it should work.
 
+If there are inter-frame dependencies, so the codec calls
+ff_thread_report/await_progress(), set AVCodecInternal.allocate_progress. The
+frames must then be freed with ff_thread_release_buffer().
+Otherwise leave it at zero and decode directly into the user-supplied frames.
+
 Call ff_thread_report_progress() after some part of the current picture has 
decoded.
 A good place to put this is where draw_horiz_band() is called - add this if it 
isn't
 called anywhere, as it's useful too and the implementation is trivial when 
you're
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 8e64640..594bd1e 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -61,6 +61,21 @@ typedef struct AVCodecInternal {
      */
     int is_copy;
 
+    /**
+     * Whether to allocate progress for frame threading.
+     *
+     * The codec must set it to 1 if it uses ff_thread_await/report_progress(),
+     * then progress will be allocated in ff_thread_get_buffer(). The frames
+     * then MUST be freed with ff_thread_release_buffer().
+     *
+     * If the codec does not need to call the progress functions (there are no
+     * dependencies between the frames), it should leave this at 0. Then it can
+     * decode straight to the user-provided frames (which the user will then
+     * free with av_frame_unref()), there is no need to call
+     * ff_thread_release_buffer().
+     */
+    int allocate_progress;
+
 #if FF_API_OLD_DECODE_AUDIO
     /**
      * Internal sample count used by avcodec_encode_audio() to fabricate pts.
diff --git a/libavcodec/pthread.c b/libavcodec/pthread.c
index 82df03c..c2be1a4 100644
--- a/libavcodec/pthread.c
+++ b/libavcodec/pthread.c
@@ -52,6 +52,7 @@
 #include "avcodec.h"
 #include "internal.h"
 #include "thread.h"
+#include "libavutil/avassert.h"
 #include "libavutil/common.h"
 
 #if HAVE_PTHREADS
@@ -80,9 +81,6 @@ typedef struct ThreadContext {
     int done;
 } ThreadContext;
 
-/// Max number of frame buffers that can be allocated when using frame threads.
-#define MAX_BUFFERS (32+1)
-
 /**
  * Context used by codec threads and stored in their AVCodecContext 
thread_opaque.
  */
@@ -122,16 +120,12 @@ typedef struct PerThreadContext {
      * Array of frames passed to ff_thread_release_buffer().
      * Frames are released after all threads referencing them are finished.
      */
-    AVFrame released_buffers[MAX_BUFFERS];
-    int     num_released_buffers;
-
-    /**
-     * Array of progress values used by ff_thread_get_buffer().
-     */
-    int     progress[MAX_BUFFERS][2];
-    uint8_t progress_used[MAX_BUFFERS];
+    AVFrame *released_buffers;
+    int  num_released_buffers;
+    int      released_buffers_allocated;
 
     AVFrame *requested_frame;       ///< AVFrame the codec passed to 
get_buffer()
+    int      requested_flags;       ///< flags passed to get_buffer() for 
requested_frame
 } PerThreadContext;
 
 /**
@@ -459,8 +453,11 @@ static int update_context_from_user(AVCodecContext *dst, 
AVCodecContext *src)
     dst->flags          = src->flags;
 
     dst->draw_horiz_band= src->draw_horiz_band;
+    dst->get_buffer2    = src->get_buffer2;
+#if FF_API_GET_BUFFER
     dst->get_buffer     = src->get_buffer;
     dst->release_buffer = src->release_buffer;
+#endif
 
     dst->opaque   = src->opaque;
     dst->debug    = src->debug;
@@ -492,28 +489,21 @@ static int update_context_from_user(AVCodecContext *dst, 
AVCodecContext *src)
 #undef copy_fields
 }
 
-static void free_progress(AVFrame *f)
-{
-    PerThreadContext *p = f->owner->thread_opaque;
-    int *progress = f->thread_opaque;
-
-    p->progress_used[(progress - p->progress[0]) / 2] = 0;
-}
-
 /// Releases the buffers that this decoding thread was the last user of.
 static void release_delayed_buffers(PerThreadContext *p)
 {
     FrameThreadContext *fctx = p->parent;
+    AVFrame *f;
 
     while (p->num_released_buffers > 0) {
-        AVFrame *f;
-
         pthread_mutex_lock(&fctx->buffer_mutex);
+
+        // fix extended data in case the caller screwed it up
+        av_assert0(p->avctx->codec_type == AVMEDIA_TYPE_VIDEO);
         f = &p->released_buffers[--p->num_released_buffers];
-        free_progress(f);
-        f->thread_opaque = NULL;
+        f->extended_data = f->data;
+        av_frame_unref(f);
 
-        f->owner->release_buffer(f->owner, f);
         pthread_mutex_unlock(&fctx->buffer_mutex);
     }
 }
@@ -567,15 +557,18 @@ static int submit_packet(PerThreadContext *p, AVPacket 
*avpkt)
      * and it calls back to the client here.
      */
 
-    if (!p->avctx->thread_safe_callbacks &&
-         p->avctx->get_buffer != avcodec_default_get_buffer) {
+    if (!p->avctx->thread_safe_callbacks && (
+#if FF_API_GET_BUFFER
+         p->avctx->get_buffer ||
+#endif
+         p->avctx->get_buffer2 != avcodec_default_get_buffer2)) {
         while (p->state != STATE_SETUP_FINISHED && p->state != 
STATE_INPUT_READY) {
             pthread_mutex_lock(&p->progress_mutex);
             while (p->state == STATE_SETTING_UP)
                 pthread_cond_wait(&p->progress_cond, &p->progress_mutex);
 
             if (p->state == STATE_GET_BUFFER) {
-                p->result = ff_get_buffer(p->avctx, p->requested_frame);
+                p->result = ff_get_buffer(p->avctx, p->requested_frame, 
p->requested_flags);
                 p->state  = STATE_SETTING_UP;
                 pthread_cond_signal(&p->progress_cond);
             }
@@ -637,7 +630,7 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
             pthread_mutex_unlock(&p->progress_mutex);
         }
 
-        *picture = p->frame;
+        av_frame_move_ref(picture, &p->frame);
         *got_picture_ptr = p->got_frame;
         picture->pkt_dts = p->avpkt.dts;
 
@@ -662,10 +655,10 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
     return (p->result >= 0) ? avpkt->size : p->result;
 }
 
-void ff_thread_report_progress(AVFrame *f, int n, int field)
+void ff_thread_report_progress(ThreadFrame *f, int n, int field)
 {
     PerThreadContext *p;
-    int *progress = f->thread_opaque;
+    int *progress = f->progress ? (int*)f->progress->data : NULL;
 
     if (!progress || progress[field] >= n) return;
 
@@ -680,10 +673,10 @@ void ff_thread_report_progress(AVFrame *f, int n, int 
field)
     pthread_mutex_unlock(&p->progress_mutex);
 }
 
-void ff_thread_await_progress(AVFrame *f, int n, int field)
+void ff_thread_await_progress(ThreadFrame *f, int n, int field)
 {
     PerThreadContext *p;
-    int *progress = f->thread_opaque;
+    int *progress = f->progress ? (int*)f->progress->data : NULL;
 
     if (!progress || progress[field] >= n) return;
 
@@ -730,7 +723,7 @@ static void frame_thread_free(AVCodecContext *avctx, int 
thread_count)
 {
     FrameThreadContext *fctx = avctx->thread_opaque;
     const AVCodec *codec = avctx->codec;
-    int i;
+    int i, j;
 
     park_frame_worker_threads(fctx, thread_count);
 
@@ -767,6 +760,7 @@ static void frame_thread_free(AVCodecContext *avctx, int 
thread_count)
         pthread_cond_destroy(&p->output_cond);
         av_buffer_unref(&p->avpkt.buf);
         av_freep(&p->buf);
+        av_freep(&p->released_buffers);
 
         if (i) {
             av_freep(&p->avctx->priv_data);
@@ -899,34 +893,15 @@ void ff_thread_flush(AVCodecContext *avctx)
     }
 }
 
-static int *allocate_progress(PerThreadContext *p)
-{
-    int i;
-
-    for (i = 0; i < MAX_BUFFERS; i++)
-        if (!p->progress_used[i]) break;
-
-    if (i == MAX_BUFFERS) {
-        av_log(p->avctx, AV_LOG_ERROR, "allocate_progress() overflow\n");
-        return NULL;
-    }
-
-    p->progress_used[i] = 1;
-
-    return p->progress[i];
-}
-
-int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame *f)
+int ff_thread_get_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags)
 {
     PerThreadContext *p = avctx->thread_opaque;
-    int *progress, err;
+    int err;
 
     f->owner = avctx;
 
-    if (!(avctx->active_thread_type&FF_THREAD_FRAME)) {
-        f->thread_opaque = NULL;
-        return ff_get_buffer(avctx, f);
-    }
+    if (!(avctx->active_thread_type & FF_THREAD_FRAME))
+        return ff_get_buffer(avctx, f->f, flags);
 
     if (p->state != STATE_SETTING_UP &&
         (avctx->codec->update_thread_context || 
!avctx->thread_safe_callbacks)) {
@@ -934,22 +909,27 @@ int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame 
*f)
         return -1;
     }
 
-    pthread_mutex_lock(&p->parent->buffer_mutex);
-    f->thread_opaque = progress = allocate_progress(p);
+    if (avctx->internal->allocate_progress) {
+        int *progress;
+        f->progress = av_buffer_alloc(2 * sizeof(int));
+        if (!f->progress) {
+            return AVERROR(ENOMEM);
+        }
+        progress = (int*)f->progress->data;
 
-    if (!progress) {
-        pthread_mutex_unlock(&p->parent->buffer_mutex);
-        return -1;
+        progress[0] = progress[1] = -1;
     }
 
-    progress[0] =
-    progress[1] = -1;
-
-    if (avctx->thread_safe_callbacks ||
-        avctx->get_buffer == avcodec_default_get_buffer) {
-        err = ff_get_buffer(avctx, f);
+    pthread_mutex_lock(&p->parent->buffer_mutex);
+    if (avctx->thread_safe_callbacks || (
+#if FF_API_GET_BUFFER
+        !avctx->get_buffer &&
+#endif
+        avctx->get_buffer2 == avcodec_default_get_buffer2)) {
+        err = ff_get_buffer(avctx, f->f, flags);
     } else {
-        p->requested_frame = f;
+        p->requested_frame = f->f;
+        p->requested_flags = flags;
         p->state = STATE_GET_BUFFER;
         pthread_mutex_lock(&p->progress_mutex);
         pthread_cond_signal(&p->progress_cond);
@@ -965,41 +945,60 @@ int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame 
*f)
             ff_thread_finish_setup(avctx);
     }
 
-    if (err) {
-        free_progress(f);
-        f->thread_opaque = NULL;
-    }
+    if (err)
+        av_buffer_unref(&f->progress);
+
     pthread_mutex_unlock(&p->parent->buffer_mutex);
 
     return err;
 }
 
-void ff_thread_release_buffer(AVCodecContext *avctx, AVFrame *f)
+void ff_thread_release_buffer(AVCodecContext *avctx, ThreadFrame *f)
 {
     PerThreadContext *p = avctx->thread_opaque;
     FrameThreadContext *fctx;
+    AVFrame *dst, *tmp;
+    int can_direct_free = !(avctx->active_thread_type & FF_THREAD_FRAME) ||
+                          avctx->thread_safe_callbacks                   ||
+                          (
+#if FF_API_GET_BUFFER
+                           !avctx->get_buffer &&
+#endif
+                           avctx->get_buffer2 == avcodec_default_get_buffer2);
 
-    if (!f->data[0])
+    if (!f->f->data[0])
         return;
 
-    if (!(avctx->active_thread_type&FF_THREAD_FRAME)) {
-        avctx->release_buffer(avctx, f);
-        return;
-    }
+    if (avctx->debug & FF_DEBUG_BUFFERS)
+        av_log(avctx, AV_LOG_DEBUG, "thread_release_buffer called on pic 
%p\n", f);
 
-    if (p->num_released_buffers >= MAX_BUFFERS) {
-        av_log(p->avctx, AV_LOG_ERROR, "too many thread_release_buffer 
calls!\n");
+    av_buffer_unref(&f->progress);
+    f->owner    = NULL;
+
+    if (can_direct_free) {
+        av_frame_unref(f->f);
         return;
     }
 
-    if(avctx->debug & FF_DEBUG_BUFFERS)
-        av_log(avctx, AV_LOG_DEBUG, "thread_release_buffer called on pic 
%p\n", f);
-
     fctx = p->parent;
     pthread_mutex_lock(&fctx->buffer_mutex);
-    p->released_buffers[p->num_released_buffers++] = *f;
+
+    if (p->num_released_buffers + 1 >= INT_MAX / sizeof(*p->released_buffers))
+        goto fail;
+    tmp = av_fast_realloc(p->released_buffers, &p->released_buffers_allocated,
+                          (p->num_released_buffers + 1) *
+                          sizeof(*p->released_buffers));
+    if (!tmp)
+        goto fail;
+    p->released_buffers = tmp;
+
+    dst = &p->released_buffers[p->num_released_buffers];
+    av_frame_move_ref(dst, f->f);
+
+    p->num_released_buffers++;
+
+fail:
     pthread_mutex_unlock(&fctx->buffer_mutex);
-    memset(f->data, 0, sizeof(f->data));
 }
 
 /**
diff --git a/libavcodec/thread.h b/libavcodec/thread.h
index 99b0ce1..864e67e 100644
--- a/libavcodec/thread.h
+++ b/libavcodec/thread.h
@@ -27,9 +27,19 @@
 #ifndef AVCODEC_THREAD_H
 #define AVCODEC_THREAD_H
 
+#include "libavutil/buffer.h"
+
 #include "config.h"
 #include "avcodec.h"
 
+typedef struct ThreadFrame {
+    AVFrame *f;
+    AVCodecContext *owner;
+    // progress->data is an array of 2 ints holding progress for top/bottom
+    // fields
+    AVBufferRef *progress;
+} ThreadFrame;
+
 /**
  * Wait for decoding threads to finish and reset internal state.
  * Called by avcodec_flush_buffers().
@@ -71,7 +81,7 @@ void ff_thread_finish_setup(AVCodecContext *avctx);
  * @param field The field being decoded, for field-picture codecs.
  * 0 for top field or frame pictures, 1 for bottom field.
  */
-void ff_thread_report_progress(AVFrame *f, int progress, int field);
+void ff_thread_report_progress(ThreadFrame *f, int progress, int field);
 
 /**
  * Wait for earlier decoding threads to finish reference pictures.
@@ -85,7 +95,7 @@ void ff_thread_report_progress(AVFrame *f, int progress, int 
field);
  * @param field The field being referenced, for field-picture codecs.
  * 0 for top field or frame pictures, 1 for bottom field.
  */
-void ff_thread_await_progress(AVFrame *f, int progress, int field);
+void ff_thread_await_progress(ThreadFrame *f, int progress, int field);
 
 /**
  * Wrapper around get_buffer() for frame-multithreaded codecs.
@@ -95,7 +105,7 @@ void ff_thread_await_progress(AVFrame *f, int progress, int 
field);
  * @param avctx The current context.
  * @param f The frame to write into.
  */
-int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame *f);
+int ff_thread_get_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags);
 
 /**
  * Wrapper around release_buffer() frame-for multithreaded codecs.
@@ -108,7 +118,9 @@ int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame *f);
  * @param avctx The current context.
  * @param f The picture being released.
  */
-void ff_thread_release_buffer(AVCodecContext *avctx, AVFrame *f);
+void ff_thread_release_buffer(AVCodecContext *avctx, ThreadFrame *f);
+
+int ff_thread_ref_frame(ThreadFrame *dst, ThreadFrame *src);
 
 int ff_thread_init(AVCodecContext *s);
 void ff_thread_free(AVCodecContext *s);
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index 253290c..c9b6aac 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -2239,6 +2239,25 @@ unsigned int avpriv_toupper4(unsigned int x)
            + (toupper((x >> 24) & 0xFF) << 24);
 }
 
+int ff_thread_ref_frame(ThreadFrame *dst, ThreadFrame *src)
+{
+    int ret;
+
+    dst->owner = src->owner;
+
+    ret = av_frame_ref(dst->f, src->f);
+    if (ret < 0)
+        return ret;
+
+    if (src->progress &&
+        !(dst->progress = av_buffer_ref(src->progress))) {
+        ff_thread_release_buffer(dst->owner, dst);
+        return AVERROR(ENOMEM);
+    }
+
+    return 0;
+}
+
 #if !HAVE_THREADS
 
 int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame *f, int flags)
-- 
1.7.10.4

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to