Browse code

pthread_frame: do not run hwaccel decoding asynchronously unless it's safe

Certain hardware decoding APIs are not guaranteed to be thread-safe, so
having the user access decoded hardware surfaces while the decoder is
running in another thread can cause failures (this is mainly known to
happen with DXVA2).

For such hwaccels, only allow the decoding thread to run while the user
is inside a lavc decode call (avcodec_send_packet/receive_frame).

Merges Libav commit d4a91e65.

Signed-off-by: wm4 <nfxjfg@googlemail.com>
Tested-by: Michael Niedermayer <michael@niedermayer.cc>

Anton Khirnov authored on 2016/11/24 23:14:22
Showing 12 changed files
... ...
@@ -3905,6 +3905,11 @@ typedef struct AVHWAccel {
3905 3905
      * AVCodecInternal.hwaccel_priv_data.
3906 3906
      */
3907 3907
     int priv_data_size;
3908
+
3909
+    /**
3910
+     * Internal hwaccel capabilities.
3911
+     */
3912
+    int caps_internal;
3908 3913
 } AVHWAccel;
3909 3914
 
3910 3915
 /**
3911 3916
new file mode 100644
... ...
@@ -0,0 +1,24 @@
0
+/*
1
+ * This file is part of FFmpeg and was stolen from Libav.
2
+ *
3
+ * FFmpeg is free software; you can redistribute it and/or
4
+ * modify it under the terms of the GNU Lesser General Public
5
+ * License as published by the Free Software Foundation; either
6
+ * version 2.1 of the License, or (at your option) any later version.
7
+ *
8
+ * FFmpeg is distributed in the hope that it will be useful,
9
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
+ * Lesser General Public License for more details.
12
+ *
13
+ * You should have received a copy of the GNU Lesser General Public
14
+ * License along with FFmpeg; if not, write to the Free Software
15
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+ */
17
+
18
+#ifndef AVCODEC_HWACCEL_H
19
+#define AVCODEC_HWACCEL_H
20
+
21
+#define HWACCEL_CAP_ASYNC_SAFE      (1 << 0)
22
+
23
+#endif /* AVCODEC_HWACCEL_H */
... ...
@@ -28,6 +28,7 @@
28 28
 #include <stdint.h>
29 29
 
30 30
 #include "avcodec.h"
31
+#include "hwaccel.h"
31 32
 #include "internal.h"
32 33
 #include "pthread_internal.h"
33 34
 #include "thread.h"
... ...
@@ -105,6 +106,7 @@ typedef struct PerThreadContext {
105 105
     int die;                        ///< Set when the thread should exit.
106 106
 
107 107
     int hwaccel_serializing;
108
+    int async_serializing;
108 109
 } PerThreadContext;
109 110
 
110 111
 /**
... ...
@@ -120,6 +122,7 @@ typedef struct FrameThreadContext {
120 120
      * is used.
121 121
      */
122 122
     pthread_mutex_t hwaccel_mutex;
123
+    pthread_mutex_t async_mutex;
123 124
 
124 125
     int next_decoding;             ///< The next context to submit a packet to.
125 126
     int next_finished;             ///< The next context to return output from.
... ...
@@ -190,6 +193,11 @@ static attribute_align_arg void *frame_worker_thread(void *arg)
190 190
             pthread_mutex_unlock(&p->parent->hwaccel_mutex);
191 191
         }
192 192
 
193
+        if (p->async_serializing) {
194
+            p->async_serializing = 0;
195
+            pthread_mutex_unlock(&p->parent->async_mutex);
196
+        }
197
+
193 198
         pthread_mutex_lock(&p->progress_mutex);
194 199
 #if 0 //BUFREF-FIXME
195 200
         for (i = 0; i < MAX_BUFFERS; i++)
... ...
@@ -443,7 +451,11 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
443 443
     FrameThreadContext *fctx = avctx->internal->thread_ctx;
444 444
     int finished = fctx->next_finished;
445 445
     PerThreadContext *p;
446
-    int err;
446
+    int err, ret;
447
+
448
+    /* release the async lock, permitting blocked hwaccel threads to
449
+     * go forward while we are in this function */
450
+    pthread_mutex_unlock(&fctx->async_mutex);
447 451
 
448 452
     /*
449 453
      * Submit a packet to the next decoding thread.
... ...
@@ -451,9 +463,11 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
451 451
 
452 452
     p = &fctx->threads[fctx->next_decoding];
453 453
     err = update_context_from_user(p->avctx, avctx);
454
-    if (err) return err;
454
+    if (err)
455
+        goto finish;
455 456
     err = submit_packet(p, avpkt);
456
-    if (err) return err;
457
+    if (err)
458
+        goto finish;
457 459
 
458 460
     /*
459 461
      * If we're still receiving the initial packets, don't return a frame.
... ...
@@ -464,8 +478,10 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
464 464
 
465 465
     if (fctx->delaying) {
466 466
         *got_picture_ptr=0;
467
-        if (avpkt->size)
468
-            return avpkt->size;
467
+        if (avpkt->size) {
468
+            ret = avpkt->size;
469
+            goto finish;
470
+        }
469 471
     }
470 472
 
471 473
     /*
... ...
@@ -515,10 +531,15 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
515 515
      * Otherwise the error can get lost.
516 516
      */
517 517
     if (!avpkt->size && !*got_picture_ptr)
518
-        return err;
518
+        goto finish;
519 519
 
520 520
     /* return the size of the consumed packet if no error occurred */
521
-    return (p->result >= 0) ? avpkt->size : p->result;
521
+    ret = (p->result >= 0) ? avpkt->size : p->result;
522
+finish:
523
+    pthread_mutex_lock(&fctx->async_mutex);
524
+    if (err < 0)
525
+        return err;
526
+    return ret;
522 527
 }
523 528
 
524 529
 void ff_thread_report_progress(ThreadFrame *f, int n, int field)
... ...
@@ -573,6 +594,13 @@ void ff_thread_finish_setup(AVCodecContext *avctx) {
573 573
         p->hwaccel_serializing = 1;
574 574
     }
575 575
 
576
+    /* this assumes that no hwaccel calls happen before ff_thread_finish_setup() */
577
+    if (avctx->hwaccel &&
578
+        !(avctx->hwaccel->caps_internal & HWACCEL_CAP_ASYNC_SAFE)) {
579
+        p->async_serializing = 1;
580
+        pthread_mutex_lock(&p->parent->async_mutex);
581
+    }
582
+
576 583
     pthread_mutex_lock(&p->progress_mutex);
577 584
     if(atomic_load(&p->state) == STATE_SETUP_FINISHED){
578 585
         av_log(avctx, AV_LOG_WARNING, "Multiple ff_thread_finish_setup() calls\n");
... ...
@@ -589,6 +617,8 @@ static void park_frame_worker_threads(FrameThreadContext *fctx, int thread_count
589 589
 {
590 590
     int i;
591 591
 
592
+    pthread_mutex_unlock(&fctx->async_mutex);
593
+
592 594
     for (i = 0; i < thread_count; i++) {
593 595
         PerThreadContext *p = &fctx->threads[i];
594 596
 
... ...
@@ -600,6 +630,8 @@ static void park_frame_worker_threads(FrameThreadContext *fctx, int thread_count
600 600
         }
601 601
         p->got_frame = 0;
602 602
     }
603
+
604
+    pthread_mutex_lock(&fctx->async_mutex);
603 605
 }
604 606
 
605 607
 void ff_frame_thread_free(AVCodecContext *avctx, int thread_count)
... ...
@@ -663,6 +695,10 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count)
663 663
     av_freep(&fctx->threads);
664 664
     pthread_mutex_destroy(&fctx->buffer_mutex);
665 665
     pthread_mutex_destroy(&fctx->hwaccel_mutex);
666
+
667
+    pthread_mutex_unlock(&fctx->async_mutex);
668
+    pthread_mutex_destroy(&fctx->async_mutex);
669
+
666 670
     av_freep(&avctx->internal->thread_ctx);
667 671
 
668 672
     if (avctx->priv_data && avctx->codec && avctx->codec->priv_class)
... ...
@@ -710,6 +746,10 @@ int ff_frame_thread_init(AVCodecContext *avctx)
710 710
 
711 711
     pthread_mutex_init(&fctx->buffer_mutex, NULL);
712 712
     pthread_mutex_init(&fctx->hwaccel_mutex, NULL);
713
+
714
+    pthread_mutex_init(&fctx->async_mutex, NULL);
715
+    pthread_mutex_lock(&fctx->async_mutex);
716
+
713 717
     fctx->delaying = 1;
714 718
 
715 719
     for (i = 0; i < thread_count; i++) {
... ...
@@ -22,6 +22,7 @@
22 22
 
23 23
 #include "h264dec.h"
24 24
 #include "h264_ps.h"
25
+#include "hwaccel.h"
25 26
 #include "vaapi_decode.h"
26 27
 
27 28
 /**
... ...
@@ -399,4 +400,5 @@ AVHWAccel ff_h264_vaapi_hwaccel = {
399 399
     .init                 = &ff_vaapi_decode_init,
400 400
     .uninit               = &ff_vaapi_decode_uninit,
401 401
     .priv_data_size       = sizeof(VAAPIDecodeContext),
402
+    .caps_internal        = HWACCEL_CAP_ASYNC_SAFE,
402 403
 };
... ...
@@ -20,6 +20,7 @@
20 20
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 21
  */
22 22
 
23
+#include "hwaccel.h"
23 24
 #include "mpegutils.h"
24 25
 #include "mpegvideo.h"
25 26
 #include "internal.h"
... ...
@@ -183,4 +184,5 @@ AVHWAccel ff_mpeg2_vaapi_hwaccel = {
183 183
     .init                 = &ff_vaapi_decode_init,
184 184
     .uninit               = &ff_vaapi_decode_uninit,
185 185
     .priv_data_size       = sizeof(VAAPIDecodeContext),
186
+    .caps_internal        = HWACCEL_CAP_ASYNC_SAFE,
186 187
 };
... ...
@@ -21,6 +21,7 @@
21 21
  */
22 22
 
23 23
 #include "h263.h"
24
+#include "hwaccel.h"
24 25
 #include "internal.h"
25 26
 #include "mpeg4video.h"
26 27
 #include "mpegvideo.h"
... ...
@@ -189,6 +190,7 @@ AVHWAccel ff_mpeg4_vaapi_hwaccel = {
189 189
     .init                 = &ff_vaapi_decode_init,
190 190
     .uninit               = &ff_vaapi_decode_uninit,
191 191
     .priv_data_size       = sizeof(VAAPIDecodeContext),
192
+    .caps_internal        = HWACCEL_CAP_ASYNC_SAFE,
192 193
 };
193 194
 #endif
194 195
 
... ...
@@ -205,5 +207,6 @@ AVHWAccel ff_h263_vaapi_hwaccel = {
205 205
     .init                 = &ff_vaapi_decode_init,
206 206
     .uninit               = &ff_vaapi_decode_uninit,
207 207
     .priv_data_size       = sizeof(VAAPIDecodeContext),
208
+    .caps_internal        = HWACCEL_CAP_ASYNC_SAFE,
208 209
 };
209 210
 #endif
... ...
@@ -20,6 +20,7 @@
20 20
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 21
  */
22 22
 
23
+#include "hwaccel.h"
23 24
 #include "internal.h"
24 25
 #include "vaapi_decode.h"
25 26
 #include "vc1.h"
... ...
@@ -399,6 +400,7 @@ AVHWAccel ff_wmv3_vaapi_hwaccel = {
399 399
     .init                 = &ff_vaapi_decode_init,
400 400
     .uninit               = &ff_vaapi_decode_uninit,
401 401
     .priv_data_size       = sizeof(VAAPIDecodeContext),
402
+    .caps_internal        = HWACCEL_CAP_ASYNC_SAFE,
402 403
 };
403 404
 #endif
404 405
 
... ...
@@ -414,4 +416,5 @@ AVHWAccel ff_vc1_vaapi_hwaccel = {
414 414
     .init                 = &ff_vaapi_decode_init,
415 415
     .uninit               = &ff_vaapi_decode_uninit,
416 416
     .priv_data_size       = sizeof(VAAPIDecodeContext),
417
+    .caps_internal        = HWACCEL_CAP_ASYNC_SAFE,
417 418
 };
... ...
@@ -27,6 +27,7 @@
27 27
 #include "internal.h"
28 28
 #include "h264dec.h"
29 29
 #include "h264_ps.h"
30
+#include "hwaccel.h"
30 31
 #include "mpegutils.h"
31 32
 #include "vdpau.h"
32 33
 #include "vdpau_internal.h"
... ...
@@ -273,4 +274,5 @@ AVHWAccel ff_h264_vdpau_hwaccel = {
273 273
     .init           = vdpau_h264_init,
274 274
     .uninit         = ff_vdpau_common_uninit,
275 275
     .priv_data_size = sizeof(VDPAUContext),
276
+    .caps_internal  = HWACCEL_CAP_ASYNC_SAFE,
276 277
 };
... ...
@@ -25,6 +25,7 @@
25 25
 #include "avcodec.h"
26 26
 #include "internal.h"
27 27
 #include "hevc.h"
28
+#include "hwaccel.h"
28 29
 #include "vdpau.h"
29 30
 #include "vdpau_internal.h"
30 31
 
... ...
@@ -423,4 +424,5 @@ AVHWAccel ff_hevc_vdpau_hwaccel = {
423 423
     .init           = vdpau_hevc_init,
424 424
     .uninit         = ff_vdpau_common_uninit,
425 425
     .priv_data_size = sizeof(VDPAUContext),
426
+    .caps_internal  = HWACCEL_CAP_ASYNC_SAFE,
426 427
 };
... ...
@@ -24,6 +24,7 @@
24 24
 #include <vdpau/vdpau.h>
25 25
 
26 26
 #include "avcodec.h"
27
+#include "hwaccel.h"
27 28
 #include "mpegvideo.h"
28 29
 #include "vdpau.h"
29 30
 #include "vdpau_internal.h"
... ...
@@ -114,6 +115,7 @@ AVHWAccel ff_mpeg1_vdpau_hwaccel = {
114 114
     .init           = vdpau_mpeg1_init,
115 115
     .uninit         = ff_vdpau_common_uninit,
116 116
     .priv_data_size = sizeof(VDPAUContext),
117
+    .caps_internal  = HWACCEL_CAP_ASYNC_SAFE,
117 118
 };
118 119
 #endif
119 120
 
... ...
@@ -148,5 +150,6 @@ AVHWAccel ff_mpeg2_vdpau_hwaccel = {
148 148
     .init           = vdpau_mpeg2_init,
149 149
     .uninit         = ff_vdpau_common_uninit,
150 150
     .priv_data_size = sizeof(VDPAUContext),
151
+    .caps_internal  = HWACCEL_CAP_ASYNC_SAFE,
151 152
 };
152 153
 #endif
... ...
@@ -24,6 +24,7 @@
24 24
 #include <vdpau/vdpau.h>
25 25
 
26 26
 #include "avcodec.h"
27
+#include "hwaccel.h"
27 28
 #include "mpeg4video.h"
28 29
 #include "vdpau.h"
29 30
 #include "vdpau_internal.h"
... ...
@@ -121,4 +122,5 @@ AVHWAccel ff_mpeg4_vdpau_hwaccel = {
121 121
     .init           = vdpau_mpeg4_init,
122 122
     .uninit         = ff_vdpau_common_uninit,
123 123
     .priv_data_size = sizeof(VDPAUContext),
124
+    .caps_internal  = HWACCEL_CAP_ASYNC_SAFE,
124 125
 };
... ...
@@ -24,6 +24,7 @@
24 24
 #include <vdpau/vdpau.h>
25 25
 
26 26
 #include "avcodec.h"
27
+#include "hwaccel.h"
27 28
 #include "vc1.h"
28 29
 #include "vdpau.h"
29 30
 #include "vdpau_internal.h"
... ...
@@ -147,6 +148,7 @@ AVHWAccel ff_wmv3_vdpau_hwaccel = {
147 147
     .init           = vdpau_vc1_init,
148 148
     .uninit         = ff_vdpau_common_uninit,
149 149
     .priv_data_size = sizeof(VDPAUContext),
150
+    .caps_internal  = HWACCEL_CAP_ASYNC_SAFE,
150 151
 };
151 152
 #endif
152 153
 
... ...
@@ -162,4 +164,5 @@ AVHWAccel ff_vc1_vdpau_hwaccel = {
162 162
     .init           = vdpau_vc1_init,
163 163
     .uninit         = ff_vdpau_common_uninit,
164 164
     .priv_data_size = sizeof(VDPAUContext),
165
+    .caps_internal  = HWACCEL_CAP_ASYNC_SAFE,
165 166
 };