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.
... | ... |
@@ -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 |
} |