Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
52866 views
1
/*
2
* This file is part of FFmpeg.
3
*
4
* FFmpeg is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2.1 of the License, or (at your option) any later version.
8
*
9
* FFmpeg is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
13
*
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with FFmpeg; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
*/
18
19
#include <mfx/mfxvideo.h>
20
#include <stdlib.h>
21
22
#include "libavutil/dict.h"
23
#include "libavutil/mem.h"
24
#include "libavutil/opt.h"
25
#include "libavcodec/qsv.h"
26
27
#include "ffmpeg.h"
28
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
static int qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
50
{
51
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
97
return 0;
98
}
99
100
static void qsv_uninit(AVCodecContext *s)
101
{
102
InputStream *ist = s->opaque;
103
QSVContext *qsv = ist->hwaccel_ctx;
104
105
av_freep(&qsv->ost->enc_ctx->hwaccel_context);
106
av_freep(&s->hwaccel_context);
107
108
av_buffer_unref(&qsv->opaque_surfaces_buf);
109
av_freep(&qsv->surface_used);
110
av_freep(&qsv->surface_ptrs);
111
112
av_freep(&qsv);
113
}
114
115
int qsv_init(AVCodecContext *s)
116
{
117
InputStream *ist = s->opaque;
118
QSVContext *qsv = ist->hwaccel_ctx;
119
AVQSVContext *hwctx_dec;
120
int ret;
121
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;
127
}
128
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)
135
return AVERROR(ENOMEM);
136
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);
141
142
av_freep(&s->hwaccel_context);
143
s->hwaccel_context = hwctx_dec;
144
145
ist->hwaccel_get_buffer = qsv_get_buffer;
146
ist->hwaccel_uninit = qsv_uninit;
147
148
return 0;
149
}
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
int qsv_transcode_init(OutputStream *ost)
184
{
185
InputStream *ist;
186
const enum AVPixelFormat *pix_fmt;
187
188
AVDictionaryEntry *e;
189
const AVOption *opt;
190
int flags = 0;
191
192
int err, i;
193
194
QSVContext *qsv = NULL;
195
AVQSVContext *hwctx = NULL;
196
mfxIMPL impl;
197
mfxVersion ver = { { 3, 1 } };
198
199
/* check if the encoder supports QSV */
200
if (!ost->enc->pix_fmts)
201
return 0;
202
for (pix_fmt = ost->enc->pix_fmts; *pix_fmt != AV_PIX_FMT_NONE; pix_fmt++)
203
if (*pix_fmt == AV_PIX_FMT_QSV)
204
break;
205
if (*pix_fmt == AV_PIX_FMT_NONE)
206
return 0;
207
208
if (strcmp(ost->avfilter, "null") || ost->source_index < 0)
209
return 0;
210
211
/* check if the decoder supports QSV and the output only goes to this stream */
212
ist = input_streams[ost->source_index];
213
if (ist->nb_filters || ist->hwaccel_id != HWACCEL_QSV ||
214
!ist->dec || !ist->dec->pix_fmts)
215
return 0;
216
for (pix_fmt = ist->dec->pix_fmts; *pix_fmt != AV_PIX_FMT_NONE; pix_fmt++)
217
if (*pix_fmt == AV_PIX_FMT_QSV)
218
break;
219
if (*pix_fmt == AV_PIX_FMT_NONE)
220
return 0;
221
222
for (i = 0; i < nb_output_streams; i++)
223
if (output_streams[i] != ost &&
224
output_streams[i]->source_index == ost->source_index)
225
return 0;
226
227
av_log(NULL, AV_LOG_VERBOSE, "Setting up QSV transcoding\n");
228
229
qsv = av_mallocz(sizeof(*qsv));
230
hwctx = av_qsv_alloc_context();
231
if (!qsv || !hwctx)
232
goto fail;
233
234
impl = choose_implementation(ist);
235
236
err = MFXInit(impl, &ver, &qsv->session);
237
if (err != MFX_ERR_NONE) {
238
av_log(NULL, AV_LOG_ERROR, "Error initializing an MFX session: %d\n", err);
239
goto fail;
240
}
241
242
e = av_dict_get(ost->encoder_opts, "flags", NULL, 0);
243
opt = av_opt_find(ost->enc_ctx, "flags", NULL, 0, 0);
244
if (e && opt)
245
av_opt_eval_flags(ost->enc_ctx, opt, e->value, &flags);
246
247
qsv->ost = ost;
248
249
hwctx->session = qsv->session;
250
hwctx->iopattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY;
251
hwctx->opaque_alloc = 1;
252
hwctx->nb_opaque_surfaces = 16;
253
254
ost->hwaccel_ctx = qsv;
255
ost->enc_ctx->hwaccel_context = hwctx;
256
ost->enc_ctx->pix_fmt = AV_PIX_FMT_QSV;
257
258
ist->hwaccel_ctx = qsv;
259
ist->dec_ctx->pix_fmt = AV_PIX_FMT_QSV;
260
ist->resample_pix_fmt = AV_PIX_FMT_QSV;
261
262
return 0;
263
264
fail:
265
av_freep(&hwctx);
266
av_freep(&qsv);
267
return AVERROR_UNKNOWN;
268
}
269
270