Browse code

ffmpeg_qsv: Fix hwaccel transcoding

Set up the encoder with a hardware context which will match the one
the decoder will use when it starts later.

Includes 02c2761973dfc886e94a60a9c7d6d30c296d5b8c, with additional
hackery to get around a3a0230a9870b9018dc7415ae5872784d524cfe5 being
skipped.

Mark Thompson authored on 2016/11/14 00:37:52
Showing 1 changed files
... ...
@@ -20,127 +20,74 @@
20 20
 #include <stdlib.h>
21 21
 
22 22
 #include "libavutil/dict.h"
23
+#include "libavutil/hwcontext.h"
24
+#include "libavutil/hwcontext_qsv.h"
23 25
 #include "libavutil/mem.h"
24 26
 #include "libavutil/opt.h"
25 27
 #include "libavcodec/qsv.h"
26 28
 
27 29
 #include "ffmpeg.h"
28 30
 
29
-typedef struct QSVContext {
30
-    OutputStream *ost;
31
-
32
-    mfxSession session;
33
-
34
-    mfxExtOpaqueSurfaceAlloc opaque_alloc;
35
-    AVBufferRef             *opaque_surfaces_buf;
36
-
37
-    uint8_t           *surface_used;
38
-    mfxFrameSurface1 **surface_ptrs;
39
-    int nb_surfaces;
40
-
41
-    mfxExtBuffer *ext_buffers[1];
42
-} QSVContext;
43
-
44
-static void buffer_release(void *opaque, uint8_t *data)
45
-{
46
-    *(uint8_t*)opaque = 0;
47
-}
48
-
49 31
 static int qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
50 32
 {
51 33
     InputStream *ist = s->opaque;
52
-    QSVContext  *qsv = ist->hwaccel_ctx;
53
-    int i;
54
-
55
-    for (i = 0; i < qsv->nb_surfaces; i++) {
56
-        if (qsv->surface_used[i])
57
-            continue;
58
-
59
-        frame->buf[0] = av_buffer_create((uint8_t*)qsv->surface_ptrs[i], sizeof(*qsv->surface_ptrs[i]),
60
-                                         buffer_release, &qsv->surface_used[i], 0);
61
-        if (!frame->buf[0])
62
-            return AVERROR(ENOMEM);
63
-        frame->data[3]       = (uint8_t*)qsv->surface_ptrs[i];
64
-        qsv->surface_used[i] = 1;
65
-        return 0;
66
-    }
67
-
68
-    return AVERROR(ENOMEM);
69
-}
70
-
71
-static int init_opaque_surf(QSVContext *qsv)
72
-{
73
-    AVQSVContext *hwctx_enc = qsv->ost->enc_ctx->hwaccel_context;
74
-    mfxFrameSurface1 *surfaces;
75
-    int i;
76
-
77
-    qsv->nb_surfaces = hwctx_enc->nb_opaque_surfaces;
78
-
79
-    qsv->opaque_surfaces_buf = av_buffer_ref(hwctx_enc->opaque_surfaces);
80
-    qsv->surface_ptrs        = av_mallocz_array(qsv->nb_surfaces, sizeof(*qsv->surface_ptrs));
81
-    qsv->surface_used        = av_mallocz_array(qsv->nb_surfaces, sizeof(*qsv->surface_used));
82
-    if (!qsv->opaque_surfaces_buf || !qsv->surface_ptrs || !qsv->surface_used)
83
-        return AVERROR(ENOMEM);
84
-
85
-    surfaces = (mfxFrameSurface1*)qsv->opaque_surfaces_buf->data;
86
-    for (i = 0; i < qsv->nb_surfaces; i++)
87
-        qsv->surface_ptrs[i] = surfaces + i;
88
-
89
-    qsv->opaque_alloc.Out.Surfaces   = qsv->surface_ptrs;
90
-    qsv->opaque_alloc.Out.NumSurface = qsv->nb_surfaces;
91
-    qsv->opaque_alloc.Out.Type       = hwctx_enc->opaque_alloc_type;
92
-
93
-    qsv->opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION;
94
-    qsv->opaque_alloc.Header.BufferSz = sizeof(qsv->opaque_alloc);
95
-    qsv->ext_buffers[0]               = (mfxExtBuffer*)&qsv->opaque_alloc;
96 34
 
97
-    return 0;
35
+    return av_hwframe_get_buffer(ist->hw_frames_ctx, frame, 0);
98 36
 }
99 37
 
100 38
 static void qsv_uninit(AVCodecContext *s)
101 39
 {
102 40
     InputStream *ist = s->opaque;
103
-    QSVContext  *qsv = ist->hwaccel_ctx;
41
+    av_buffer_unref(&ist->hw_frames_ctx);
42
+}
104 43
 
105
-    av_freep(&qsv->ost->enc_ctx->hwaccel_context);
106
-    av_freep(&s->hwaccel_context);
44
+static int qsv_device_init(InputStream *ist)
45
+{
46
+    int err;
107 47
 
108
-    av_buffer_unref(&qsv->opaque_surfaces_buf);
109
-    av_freep(&qsv->surface_used);
110
-    av_freep(&qsv->surface_ptrs);
48
+    err = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_QSV,
49
+                                 ist->hwaccel_device, NULL, 0);
50
+    if (err < 0) {
51
+        av_log(NULL, AV_LOG_ERROR, "Error creating a QSV device\n");
52
+        return err;
53
+    }
111 54
 
112
-    av_freep(&qsv);
55
+    return 0;
113 56
 }
114 57
 
115 58
 int qsv_init(AVCodecContext *s)
116 59
 {
117 60
     InputStream *ist = s->opaque;
118
-    QSVContext  *qsv = ist->hwaccel_ctx;
119
-    AVQSVContext *hwctx_dec;
61
+    AVHWFramesContext *frames_ctx;
62
+    AVQSVFramesContext *frames_hwctx;
120 63
     int ret;
121 64
 
122
-    if (!qsv) {
123
-        av_log(NULL, AV_LOG_ERROR, "QSV transcoding is not initialized. "
124
-               "-hwaccel qsv should only be used for one-to-one QSV transcoding "
125
-               "with no filters.\n");
126
-        return AVERROR_BUG;
65
+    if (!hw_device_ctx) {
66
+        ret = qsv_device_init(ist);
67
+        if (ret < 0)
68
+            return ret;
127 69
     }
128 70
 
129
-    ret = init_opaque_surf(qsv);
130
-    if (ret < 0)
131
-        return ret;
132
-
133
-    hwctx_dec = av_qsv_alloc_context();
134
-    if (!hwctx_dec)
71
+    av_buffer_unref(&ist->hw_frames_ctx);
72
+    ist->hw_frames_ctx = av_hwframe_ctx_alloc(hw_device_ctx);
73
+    if (!ist->hw_frames_ctx)
135 74
         return AVERROR(ENOMEM);
136 75
 
137
-    hwctx_dec->session        = qsv->session;
138
-    hwctx_dec->iopattern      = MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
139
-    hwctx_dec->ext_buffers    = qsv->ext_buffers;
140
-    hwctx_dec->nb_ext_buffers = FF_ARRAY_ELEMS(qsv->ext_buffers);
76
+    frames_ctx   = (AVHWFramesContext*)ist->hw_frames_ctx->data;
77
+    frames_hwctx = frames_ctx->hwctx;
78
+
79
+    frames_ctx->width             = FFALIGN(s->coded_width,  32);
80
+    frames_ctx->height            = FFALIGN(s->coded_height, 32);
81
+    frames_ctx->format            = AV_PIX_FMT_QSV;
82
+    frames_ctx->sw_format         = s->sw_pix_fmt;
83
+    frames_ctx->initial_pool_size = 64;
84
+    frames_hwctx->frame_type      = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;
141 85
 
142
-    av_freep(&s->hwaccel_context);
143
-    s->hwaccel_context = hwctx_dec;
86
+    ret = av_hwframe_ctx_init(ist->hw_frames_ctx);
87
+    if (ret < 0) {
88
+        av_log(NULL, AV_LOG_ERROR, "Error initializing a QSV frame pool\n");
89
+        return ret;
90
+    }
144 91
 
145 92
     ist->hwaccel_get_buffer = qsv_get_buffer;
146 93
     ist->hwaccel_uninit     = qsv_uninit;
... ...
@@ -148,53 +95,15 @@ int qsv_init(AVCodecContext *s)
148 148
     return 0;
149 149
 }
150 150
 
151
-static mfxIMPL choose_implementation(const InputStream *ist)
152
-{
153
-    static const struct {
154
-        const char *name;
155
-        mfxIMPL     impl;
156
-    } impl_map[] = {
157
-        { "auto",     MFX_IMPL_AUTO         },
158
-        { "sw",       MFX_IMPL_SOFTWARE     },
159
-        { "hw",       MFX_IMPL_HARDWARE     },
160
-        { "auto_any", MFX_IMPL_AUTO_ANY     },
161
-        { "hw_any",   MFX_IMPL_HARDWARE_ANY },
162
-        { "hw2",      MFX_IMPL_HARDWARE2    },
163
-        { "hw3",      MFX_IMPL_HARDWARE3    },
164
-        { "hw4",      MFX_IMPL_HARDWARE4    },
165
-    };
166
-
167
-    mfxIMPL impl = MFX_IMPL_AUTO_ANY;
168
-    int i;
169
-
170
-    if (ist->hwaccel_device) {
171
-        for (i = 0; i < FF_ARRAY_ELEMS(impl_map); i++)
172
-            if (!strcmp(ist->hwaccel_device, impl_map[i].name)) {
173
-                impl = impl_map[i].impl;
174
-                break;
175
-            }
176
-        if (i == FF_ARRAY_ELEMS(impl_map))
177
-            impl = strtol(ist->hwaccel_device, NULL, 0);
178
-    }
179
-
180
-    return impl;
181
-}
182
-
183 151
 int qsv_transcode_init(OutputStream *ost)
184 152
 {
185 153
     InputStream *ist;
186 154
     const enum AVPixelFormat *pix_fmt;
187 155
 
188
-    AVDictionaryEntry *e;
189
-    const AVOption *opt;
190
-    int flags = 0;
191
-
192 156
     int err, i;
193
-
194
-    QSVContext *qsv = NULL;
195
-    AVQSVContext *hwctx = NULL;
196
-    mfxIMPL impl;
197
-    mfxVersion ver = { { 3, 1 } };
157
+    AVBufferRef *encode_frames_ref = NULL;
158
+    AVHWFramesContext *encode_frames;
159
+    AVQSVFramesContext *qsv_frames;
198 160
 
199 161
     /* check if the encoder supports QSV */
200 162
     if (!ost->enc->pix_fmts)
... ...
@@ -225,43 +134,45 @@ int qsv_transcode_init(OutputStream *ost)
225 225
 
226 226
     av_log(NULL, AV_LOG_VERBOSE, "Setting up QSV transcoding\n");
227 227
 
228
-    qsv   = av_mallocz(sizeof(*qsv));
229
-    hwctx = av_qsv_alloc_context();
230
-    if (!qsv || !hwctx)
231
-        goto fail;
228
+    if (!hw_device_ctx) {
229
+        err = qsv_device_init(ist);
230
+        if (err < 0)
231
+            goto fail;
232
+    }
232 233
 
233
-    impl = choose_implementation(ist);
234
+    // This creates a dummy hw_frames_ctx for the encoder to be
235
+    // suitably initialised.  It only contains one real frame, so
236
+    // hopefully doesn't waste too much memory.
234 237
 
235
-    err = MFXInit(impl, &ver, &qsv->session);
236
-    if (err != MFX_ERR_NONE) {
237
-        av_log(NULL, AV_LOG_ERROR, "Error initializing an MFX session: %d\n", err);
238
+    encode_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx);
239
+    if (!encode_frames_ref) {
240
+        err = AVERROR(ENOMEM);
238 241
         goto fail;
239 242
     }
243
+    encode_frames = (AVHWFramesContext*)encode_frames_ref->data;
244
+    qsv_frames = encode_frames->hwctx;
240 245
 
241
-    e = av_dict_get(ost->encoder_opts, "flags", NULL, 0);
242
-    opt = av_opt_find(ost->enc_ctx, "flags", NULL, 0, 0);
243
-    if (e && opt)
244
-        av_opt_eval_flags(ost->enc_ctx, opt, e->value, &flags);
246
+    encode_frames->width     = FFALIGN(ist->resample_width,  32);
247
+    encode_frames->height    = FFALIGN(ist->resample_height, 32);
248
+    encode_frames->format    = AV_PIX_FMT_QSV;
249
+    encode_frames->sw_format = AV_PIX_FMT_NV12;
250
+    encode_frames->initial_pool_size = 1;
245 251
 
246
-    qsv->ost = ost;
252
+    qsv_frames->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;
247 253
 
248
-    hwctx->session                = qsv->session;
249
-    hwctx->iopattern              = MFX_IOPATTERN_IN_OPAQUE_MEMORY;
250
-    hwctx->opaque_alloc           = 1;
251
-    hwctx->nb_opaque_surfaces     = 16;
254
+    err = av_hwframe_ctx_init(encode_frames_ref);
255
+    if (err < 0)
256
+        goto fail;
252 257
 
253
-    ost->hwaccel_ctx              = qsv;
254
-    ost->enc_ctx->hwaccel_context = hwctx;
255
-    ost->enc_ctx->pix_fmt         = AV_PIX_FMT_QSV;
258
+    ist->dec_ctx->pix_fmt       = AV_PIX_FMT_QSV;
259
+    ist->resample_pix_fmt       = AV_PIX_FMT_QSV;
256 260
 
257
-    ist->hwaccel_ctx              = qsv;
258
-    ist->dec_ctx->pix_fmt         = AV_PIX_FMT_QSV;
259
-    ist->resample_pix_fmt         = AV_PIX_FMT_QSV;
261
+    ost->enc_ctx->pix_fmt       = AV_PIX_FMT_QSV;
262
+    ost->enc_ctx->hw_frames_ctx = encode_frames_ref;
260 263
 
261 264
     return 0;
262 265
 
263 266
 fail:
264
-    av_freep(&hwctx);
265
-    av_freep(&qsv);
266
-    return AVERROR_UNKNOWN;
267
+    av_buffer_unref(&encode_frames_ref);
268
+    return err;
267 269
 }