* commit 'fb472e1a11a4e0caed2c3c91da01ea8e35d9e3f8': avconv: add support for Intel QSV-accelerated transcoding Merged-by: Hendrik Leppkes <h.leppkes@gmail.com>
		
			
				
	
	
		
			269 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This file is part of FFmpeg.
 | 
						|
 *
 | 
						|
 * FFmpeg is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU Lesser General Public
 | 
						|
 * License as published by the Free Software Foundation; either
 | 
						|
 * version 2.1 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * FFmpeg is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with FFmpeg; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
 | 
						|
#include <mfx/mfxvideo.h>
 | 
						|
#include <stdlib.h>
 | 
						|
 | 
						|
#include "libavutil/dict.h"
 | 
						|
#include "libavutil/mem.h"
 | 
						|
#include "libavutil/opt.h"
 | 
						|
#include "libavcodec/qsv.h"
 | 
						|
 | 
						|
#include "ffmpeg.h"
 | 
						|
 | 
						|
typedef struct QSVContext {
 | 
						|
    OutputStream *ost;
 | 
						|
 | 
						|
    mfxSession session;
 | 
						|
 | 
						|
    mfxExtOpaqueSurfaceAlloc opaque_alloc;
 | 
						|
    AVBufferRef             *opaque_surfaces_buf;
 | 
						|
 | 
						|
    uint8_t           *surface_used;
 | 
						|
    mfxFrameSurface1 **surface_ptrs;
 | 
						|
    int nb_surfaces;
 | 
						|
 | 
						|
    mfxExtBuffer *ext_buffers[1];
 | 
						|
} QSVContext;
 | 
						|
 | 
						|
static void buffer_release(void *opaque, uint8_t *data)
 | 
						|
{
 | 
						|
    *(uint8_t*)opaque = 0;
 | 
						|
}
 | 
						|
 | 
						|
static int qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
 | 
						|
{
 | 
						|
    InputStream *ist = s->opaque;
 | 
						|
    QSVContext  *qsv = ist->hwaccel_ctx;
 | 
						|
    int i;
 | 
						|
 | 
						|
    for (i = 0; i < qsv->nb_surfaces; i++) {
 | 
						|
        if (qsv->surface_used[i])
 | 
						|
            continue;
 | 
						|
 | 
						|
        frame->buf[0] = av_buffer_create((uint8_t*)qsv->surface_ptrs[i], sizeof(*qsv->surface_ptrs[i]),
 | 
						|
                                         buffer_release, &qsv->surface_used[i], 0);
 | 
						|
        if (!frame->buf[0])
 | 
						|
            return AVERROR(ENOMEM);
 | 
						|
        frame->data[3]       = (uint8_t*)qsv->surface_ptrs[i];
 | 
						|
        qsv->surface_used[i] = 1;
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    return AVERROR(ENOMEM);
 | 
						|
}
 | 
						|
 | 
						|
static int init_opaque_surf(QSVContext *qsv)
 | 
						|
{
 | 
						|
    AVQSVContext *hwctx_enc = qsv->ost->enc_ctx->hwaccel_context;
 | 
						|
    mfxFrameSurface1 *surfaces;
 | 
						|
    int i;
 | 
						|
 | 
						|
    qsv->nb_surfaces = hwctx_enc->nb_opaque_surfaces;
 | 
						|
 | 
						|
    qsv->opaque_surfaces_buf = av_buffer_ref(hwctx_enc->opaque_surfaces);
 | 
						|
    qsv->surface_ptrs        = av_mallocz_array(qsv->nb_surfaces, sizeof(*qsv->surface_ptrs));
 | 
						|
    qsv->surface_used        = av_mallocz_array(qsv->nb_surfaces, sizeof(*qsv->surface_used));
 | 
						|
    if (!qsv->opaque_surfaces_buf || !qsv->surface_ptrs || !qsv->surface_used)
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
 | 
						|
    surfaces = (mfxFrameSurface1*)qsv->opaque_surfaces_buf->data;
 | 
						|
    for (i = 0; i < qsv->nb_surfaces; i++)
 | 
						|
        qsv->surface_ptrs[i] = surfaces + i;
 | 
						|
 | 
						|
    qsv->opaque_alloc.Out.Surfaces   = qsv->surface_ptrs;
 | 
						|
    qsv->opaque_alloc.Out.NumSurface = qsv->nb_surfaces;
 | 
						|
    qsv->opaque_alloc.Out.Type       = hwctx_enc->opaque_alloc_type;
 | 
						|
 | 
						|
    qsv->opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION;
 | 
						|
    qsv->opaque_alloc.Header.BufferSz = sizeof(qsv->opaque_alloc);
 | 
						|
    qsv->ext_buffers[0]               = (mfxExtBuffer*)&qsv->opaque_alloc;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void qsv_uninit(AVCodecContext *s)
 | 
						|
{
 | 
						|
    InputStream *ist = s->opaque;
 | 
						|
    QSVContext  *qsv = ist->hwaccel_ctx;
 | 
						|
 | 
						|
    av_freep(&qsv->ost->enc_ctx->hwaccel_context);
 | 
						|
    av_freep(&s->hwaccel_context);
 | 
						|
 | 
						|
    av_buffer_unref(&qsv->opaque_surfaces_buf);
 | 
						|
    av_freep(&qsv->surface_used);
 | 
						|
    av_freep(&qsv->surface_ptrs);
 | 
						|
 | 
						|
    av_freep(&qsv);
 | 
						|
}
 | 
						|
 | 
						|
int qsv_init(AVCodecContext *s)
 | 
						|
{
 | 
						|
    InputStream *ist = s->opaque;
 | 
						|
    QSVContext  *qsv = ist->hwaccel_ctx;
 | 
						|
    AVQSVContext *hwctx_dec;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    if (!qsv) {
 | 
						|
        av_log(NULL, AV_LOG_ERROR, "QSV transcoding is not initialized. "
 | 
						|
               "-hwaccel qsv should only be used for one-to-one QSV transcoding "
 | 
						|
               "with no filters.\n");
 | 
						|
        return AVERROR_BUG;
 | 
						|
    }
 | 
						|
 | 
						|
    ret = init_opaque_surf(qsv);
 | 
						|
    if (ret < 0)
 | 
						|
        return ret;
 | 
						|
 | 
						|
    hwctx_dec = av_qsv_alloc_context();
 | 
						|
    if (!hwctx_dec)
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
 | 
						|
    hwctx_dec->session        = qsv->session;
 | 
						|
    hwctx_dec->iopattern      = MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
 | 
						|
    hwctx_dec->ext_buffers    = qsv->ext_buffers;
 | 
						|
    hwctx_dec->nb_ext_buffers = FF_ARRAY_ELEMS(qsv->ext_buffers);
 | 
						|
 | 
						|
    av_freep(&s->hwaccel_context);
 | 
						|
    s->hwaccel_context = hwctx_dec;
 | 
						|
 | 
						|
    ist->hwaccel_get_buffer = qsv_get_buffer;
 | 
						|
    ist->hwaccel_uninit     = qsv_uninit;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static mfxIMPL choose_implementation(const InputStream *ist)
 | 
						|
{
 | 
						|
    static const struct {
 | 
						|
        const char *name;
 | 
						|
        mfxIMPL     impl;
 | 
						|
    } impl_map[] = {
 | 
						|
        { "auto",     MFX_IMPL_AUTO         },
 | 
						|
        { "sw",       MFX_IMPL_SOFTWARE     },
 | 
						|
        { "hw",       MFX_IMPL_HARDWARE     },
 | 
						|
        { "auto_any", MFX_IMPL_AUTO_ANY     },
 | 
						|
        { "hw_any",   MFX_IMPL_HARDWARE_ANY },
 | 
						|
        { "hw2",      MFX_IMPL_HARDWARE2    },
 | 
						|
        { "hw3",      MFX_IMPL_HARDWARE3    },
 | 
						|
        { "hw4",      MFX_IMPL_HARDWARE4    },
 | 
						|
    };
 | 
						|
 | 
						|
    mfxIMPL impl = MFX_IMPL_AUTO_ANY;
 | 
						|
    int i;
 | 
						|
 | 
						|
    if (ist->hwaccel_device) {
 | 
						|
        for (i = 0; i < FF_ARRAY_ELEMS(impl_map); i++)
 | 
						|
            if (!strcmp(ist->hwaccel_device, impl_map[i].name)) {
 | 
						|
                impl = impl_map[i].impl;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        if (i == FF_ARRAY_ELEMS(impl_map))
 | 
						|
            impl = strtol(ist->hwaccel_device, NULL, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    return impl;
 | 
						|
}
 | 
						|
 | 
						|
int qsv_transcode_init(OutputStream *ost)
 | 
						|
{
 | 
						|
    InputStream *ist;
 | 
						|
    const enum AVPixelFormat *pix_fmt;
 | 
						|
 | 
						|
    AVDictionaryEntry *e;
 | 
						|
    const AVOption *opt;
 | 
						|
    int flags = 0;
 | 
						|
 | 
						|
    int err, i;
 | 
						|
 | 
						|
    QSVContext *qsv = NULL;
 | 
						|
    AVQSVContext *hwctx = NULL;
 | 
						|
    mfxIMPL impl;
 | 
						|
    mfxVersion ver = { { 3, 1 } };
 | 
						|
 | 
						|
    /* check if the encoder supports QSV */
 | 
						|
    if (!ost->enc->pix_fmts)
 | 
						|
        return 0;
 | 
						|
    for (pix_fmt = ost->enc->pix_fmts; *pix_fmt != AV_PIX_FMT_NONE; pix_fmt++)
 | 
						|
        if (*pix_fmt == AV_PIX_FMT_QSV)
 | 
						|
            break;
 | 
						|
    if (*pix_fmt == AV_PIX_FMT_NONE)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    if (strcmp(ost->avfilter, "null") || ost->source_index < 0)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    /* check if the decoder supports QSV and the output only goes to this stream */
 | 
						|
    ist = input_streams[ost->source_index];
 | 
						|
    if (ist->nb_filters || ist->hwaccel_id != HWACCEL_QSV ||
 | 
						|
        !ist->dec || !ist->dec->pix_fmts)
 | 
						|
        return 0;
 | 
						|
    for (pix_fmt = ist->dec->pix_fmts; *pix_fmt != AV_PIX_FMT_NONE; pix_fmt++)
 | 
						|
        if (*pix_fmt == AV_PIX_FMT_QSV)
 | 
						|
            break;
 | 
						|
    if (*pix_fmt == AV_PIX_FMT_NONE)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    for (i = 0; i < nb_output_streams; i++)
 | 
						|
        if (output_streams[i] != ost &&
 | 
						|
            output_streams[i]->source_index == ost->source_index)
 | 
						|
            return 0;
 | 
						|
 | 
						|
    av_log(NULL, AV_LOG_VERBOSE, "Setting up QSV transcoding\n");
 | 
						|
 | 
						|
    qsv   = av_mallocz(sizeof(*qsv));
 | 
						|
    hwctx = av_qsv_alloc_context();
 | 
						|
    if (!qsv || !hwctx)
 | 
						|
        goto fail;
 | 
						|
 | 
						|
    impl = choose_implementation(ist);
 | 
						|
 | 
						|
    err = MFXInit(impl, &ver, &qsv->session);
 | 
						|
    if (err != MFX_ERR_NONE) {
 | 
						|
        av_log(NULL, AV_LOG_ERROR, "Error initializing an MFX session: %d\n", err);
 | 
						|
        goto fail;
 | 
						|
    }
 | 
						|
 | 
						|
    e = av_dict_get(ost->encoder_opts, "flags", NULL, 0);
 | 
						|
    opt = av_opt_find(ost->enc_ctx, "flags", NULL, 0, 0);
 | 
						|
    if (e && opt)
 | 
						|
        av_opt_eval_flags(ost->enc_ctx, opt, e->value, &flags);
 | 
						|
 | 
						|
    qsv->ost = ost;
 | 
						|
 | 
						|
    hwctx->session                = qsv->session;
 | 
						|
    hwctx->iopattern              = MFX_IOPATTERN_IN_OPAQUE_MEMORY;
 | 
						|
    hwctx->opaque_alloc           = 1;
 | 
						|
    hwctx->nb_opaque_surfaces     = 16;
 | 
						|
 | 
						|
    ost->hwaccel_ctx              = qsv;
 | 
						|
    ost->enc_ctx->hwaccel_context = hwctx;
 | 
						|
    ost->enc_ctx->pix_fmt         = AV_PIX_FMT_QSV;
 | 
						|
 | 
						|
    ist->hwaccel_ctx              = qsv;
 | 
						|
    ist->dec_ctx->pix_fmt         = AV_PIX_FMT_QSV;
 | 
						|
    ist->resample_pix_fmt         = AV_PIX_FMT_QSV;
 | 
						|
 | 
						|
    return 0;
 | 
						|
 | 
						|
fail:
 | 
						|
    av_freep(&hwctx);
 | 
						|
    av_freep(&qsv);
 | 
						|
    return AVERROR_UNKNOWN;
 | 
						|
}
 |