| @@ -0,0 +1,268 @@ |
| | 1 | /* |
| | 2 | * This file is part of Libav. |
| | 3 | * |
| | 4 | * Libav 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 | * Libav 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 Libav; 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 "avconv.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 | } |