shithub: dav1d

Download patch

ref: 7718c4644edeef78cdbc6fb93397ea3f9f4c736e
parent: b735b80b743d5bb85059e8fb87efd0375bc5bc65
author: Janne Grunau <janne-vlc@jannau.net>
date: Tue Dec 4 18:45:16 EST 2018

frame-mt: drain delayed pictures fully before returning -EAGAIN

Fixes #211, dav1d_get_picture starts draining delayed pictures when it
gets called multiple times without dav1d_send_data call in between.

--- a/src/internal.h
+++ b/src/internal.h
@@ -119,6 +119,7 @@
     int operating_point;
     unsigned operating_point_idc;
     int all_layers;
+    int drain;
 };
 
 struct Dav1dFrameContext {
--- a/src/lib.c
+++ b/src/lib.c
@@ -222,6 +222,8 @@
     validate_input_or_ret(in != NULL, -EINVAL);
     validate_input_or_ret(in->data == NULL || in->sz, -EINVAL);
 
+    c->drain = 0;
+
     if (c->in.data)
         return -EAGAIN;
     dav1d_data_move_ref(&c->in, in);
@@ -275,6 +277,35 @@
     return 0;
 }
 
+static int drain_picture(Dav1dContext *const c, Dav1dPicture *const out) {
+    unsigned drain_count = 0;
+    do {
+        const unsigned next = c->frame_thread.next;
+        Dav1dFrameContext *const f = &c->fc[next];
+        pthread_mutex_lock(&f->frame_thread.td.lock);
+        while (f->n_tile_data > 0)
+            pthread_cond_wait(&f->frame_thread.td.cond,
+                              &f->frame_thread.td.lock);
+        pthread_mutex_unlock(&f->frame_thread.td.lock);
+        Dav1dThreadPicture *const out_delayed =
+            &c->frame_thread.out_delayed[next];
+        if (++c->frame_thread.next == c->n_fc)
+            c->frame_thread.next = 0;
+        if (out_delayed->p.data[0]) {
+            const unsigned progress =
+                atomic_load_explicit(&out_delayed->progress[1],
+                                     memory_order_relaxed);
+            if (out_delayed->visible && progress != FRAME_ERROR)
+                dav1d_picture_ref(&c->out, &out_delayed->p);
+            dav1d_thread_picture_unref(out_delayed);
+            if (c->out.data[0])
+                return output_image(c, out, &c->out);
+        }
+    } while (++drain_count < c->n_fc);
+
+    return -EAGAIN;
+}
+
 int dav1d_get_picture(Dav1dContext *const c, Dav1dPicture *const out)
 {
     int res;
@@ -282,36 +313,13 @@
     validate_input_or_ret(c != NULL, -EINVAL);
     validate_input_or_ret(out != NULL, -EINVAL);
 
+    const int drain = c->drain;
+    c->drain = 1;
+
     Dav1dData *const in = &c->in;
     if (!in->data) {
         if (c->n_fc == 1) return -EAGAIN;
-
-        // flush
-        unsigned flush_count = 0;
-        do {
-            const unsigned next = c->frame_thread.next;
-            Dav1dFrameContext *const f = &c->fc[next];
-
-            pthread_mutex_lock(&f->frame_thread.td.lock);
-            while (f->n_tile_data > 0)
-                pthread_cond_wait(&f->frame_thread.td.cond,
-                                  &f->frame_thread.td.lock);
-            pthread_mutex_unlock(&f->frame_thread.td.lock);
-            Dav1dThreadPicture *const out_delayed =
-                &c->frame_thread.out_delayed[next];
-            if (++c->frame_thread.next == c->n_fc)
-                c->frame_thread.next = 0;
-            if (out_delayed->p.data[0]) {
-                const unsigned progress = atomic_load_explicit(&out_delayed->progress[1],
-                                                               memory_order_relaxed);
-                if (out_delayed->visible && progress != FRAME_ERROR)
-                    dav1d_picture_ref(&c->out, &out_delayed->p);
-                dav1d_thread_picture_unref(out_delayed);
-                if (c->out.data[0])
-                    return output_image(c, out, &c->out);
-            }
-        } while (++flush_count < c->n_fc);
-        return -EAGAIN;
+        return drain_picture(c, out);
     }
 
     while (in->sz > 0) {
@@ -333,11 +341,15 @@
     if (c->out.data[0])
         return output_image(c, out, &c->out);
 
+    if (c->n_fc > 1 && drain)
+        return drain_picture(c, out);
+
     return -EAGAIN;
 }
 
 void dav1d_flush(Dav1dContext *const c) {
     dav1d_data_unref(&c->in);
+    c->drain = 0;
 
     if (c->n_fc == 1) return;