diff --git a/configure b/configure index 647087802a..beab6ba687 100755 --- a/configure +++ b/configure @@ -2454,7 +2454,7 @@ encode_video_example_deps="avcodec avutil" filter_audio_example_deps="avfilter avutil" metadata_example_deps="avformat avutil" output_example_deps="avcodec avformat avutil swscale" -qsvdec_example_deps="avcodec avutil libmfx h264_qsv_decoder vaapi_x11" +qsvdec_example_deps="avcodec avutil libmfx h264_qsv_decoder" transcode_aac_example_deps="avcodec avformat avresample" # libraries, in linking order diff --git a/doc/examples/qsvdec.c b/doc/examples/qsvdec.c index aaecd81d4c..141c581f7f 100644 --- a/doc/examples/qsvdec.c +++ b/doc/examples/qsvdec.c @@ -26,185 +26,55 @@ * * @example qsvdec.c * This example shows how to do QSV-accelerated H.264 decoding with output - * frames in the VA-API video surfaces. + * frames in the GPU video surfaces. */ #include "config.h" #include -#include - -#include -#include -#include - #include "libavformat/avformat.h" #include "libavformat/avio.h" #include "libavcodec/avcodec.h" -#include "libavcodec/qsv.h" +#include "libavutil/buffer.h" #include "libavutil/error.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_qsv.h" #include "libavutil/mem.h" typedef struct DecodeContext { - mfxSession mfx_session; - VADisplay va_dpy; - - VASurfaceID *surfaces; - mfxMemId *surface_ids; - int *surface_used; - int nb_surfaces; - - mfxFrameInfo frame_info; + AVBufferRef *hw_device_ref; } DecodeContext; -static mfxStatus frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *req, - mfxFrameAllocResponse *resp) -{ - DecodeContext *decode = pthis; - int err, i; - - if (decode->surfaces) { - fprintf(stderr, "Multiple allocation requests.\n"); - return MFX_ERR_MEMORY_ALLOC; - } - if (!(req->Type & MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET)) { - fprintf(stderr, "Unsupported surface type: %d\n", req->Type); - return MFX_ERR_UNSUPPORTED; - } - if (req->Info.BitDepthLuma != 8 || req->Info.BitDepthChroma != 8 || - req->Info.Shift || req->Info.FourCC != MFX_FOURCC_NV12 || - req->Info.ChromaFormat != MFX_CHROMAFORMAT_YUV420) { - fprintf(stderr, "Unsupported surface properties.\n"); - return MFX_ERR_UNSUPPORTED; - } - - decode->surfaces = av_malloc_array (req->NumFrameSuggested, sizeof(*decode->surfaces)); - decode->surface_ids = av_malloc_array (req->NumFrameSuggested, sizeof(*decode->surface_ids)); - decode->surface_used = av_mallocz_array(req->NumFrameSuggested, sizeof(*decode->surface_used)); - if (!decode->surfaces || !decode->surface_ids || !decode->surface_used) - goto fail; - - err = vaCreateSurfaces(decode->va_dpy, VA_RT_FORMAT_YUV420, - req->Info.Width, req->Info.Height, - decode->surfaces, req->NumFrameSuggested, - NULL, 0); - if (err != VA_STATUS_SUCCESS) { - fprintf(stderr, "Error allocating VA surfaces\n"); - goto fail; - } - decode->nb_surfaces = req->NumFrameSuggested; - - for (i = 0; i < decode->nb_surfaces; i++) - decode->surface_ids[i] = &decode->surfaces[i]; - - resp->mids = decode->surface_ids; - resp->NumFrameActual = decode->nb_surfaces; - - decode->frame_info = req->Info; - - return MFX_ERR_NONE; -fail: - av_freep(&decode->surfaces); - av_freep(&decode->surface_ids); - av_freep(&decode->surface_used); - - return MFX_ERR_MEMORY_ALLOC; -} - -static mfxStatus frame_free(mfxHDL pthis, mfxFrameAllocResponse *resp) -{ - return MFX_ERR_NONE; -} - -static mfxStatus frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) -{ - return MFX_ERR_UNSUPPORTED; -} - -static mfxStatus frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) -{ - return MFX_ERR_UNSUPPORTED; -} - -static mfxStatus frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl) -{ - *hdl = mid; - return MFX_ERR_NONE; -} - -static void free_surfaces(DecodeContext *decode) -{ - if (decode->surfaces) - vaDestroySurfaces(decode->va_dpy, decode->surfaces, decode->nb_surfaces); - av_freep(&decode->surfaces); - av_freep(&decode->surface_ids); - av_freep(&decode->surface_used); - decode->nb_surfaces = 0; -} - -static void free_buffer(void *opaque, uint8_t *data) -{ - int *used = opaque; - *used = 0; - av_freep(&data); -} - -static int get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) -{ - DecodeContext *decode = avctx->opaque; - - mfxFrameSurface1 *surf; - AVBufferRef *surf_buf; - int idx; - - for (idx = 0; idx < decode->nb_surfaces; idx++) { - if (!decode->surface_used[idx]) - break; - } - if (idx == decode->nb_surfaces) { - fprintf(stderr, "No free surfaces\n"); - return AVERROR(ENOMEM); - } - - surf = av_mallocz(sizeof(*surf)); - if (!surf) - return AVERROR(ENOMEM); - surf_buf = av_buffer_create((uint8_t*)surf, sizeof(*surf), free_buffer, - &decode->surface_used[idx], AV_BUFFER_FLAG_READONLY); - if (!surf_buf) { - av_freep(&surf); - return AVERROR(ENOMEM); - } - - surf->Info = decode->frame_info; - surf->Data.MemId = &decode->surfaces[idx]; - - frame->buf[0] = surf_buf; - frame->data[3] = (uint8_t*)surf; - - decode->surface_used[idx] = 1; - - return 0; -} - static int get_format(AVCodecContext *avctx, const enum AVPixelFormat *pix_fmts) { while (*pix_fmts != AV_PIX_FMT_NONE) { if (*pix_fmts == AV_PIX_FMT_QSV) { - if (!avctx->hwaccel_context) { - DecodeContext *decode = avctx->opaque; - AVQSVContext *qsv = av_qsv_alloc_context(); - if (!qsv) - return AV_PIX_FMT_NONE; + DecodeContext *decode = avctx->opaque; + AVHWFramesContext *frames_ctx; + AVQSVFramesContext *frames_hwctx; + int ret; - qsv->session = decode->mfx_session; - qsv->iopattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY; + /* create a pool of surfaces to be used by the decoder */ + avctx->hw_frames_ctx = av_hwframe_ctx_alloc(decode->hw_device_ref); + if (!avctx->hw_frames_ctx) + return AV_PIX_FMT_NONE; + frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + frames_hwctx = frames_ctx->hwctx; - avctx->hwaccel_context = qsv; - } + frames_ctx->format = AV_PIX_FMT_QSV; + frames_ctx->sw_format = avctx->sw_pix_fmt; + frames_ctx->width = FFALIGN(avctx->coded_width, 32); + frames_ctx->height = FFALIGN(avctx->coded_height, 32); + frames_ctx->initial_pool_size = 32; + + frames_hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; + + ret = av_hwframe_ctx_init(avctx->hw_frames_ctx); + if (ret < 0) + return AV_PIX_FMT_NONE; return AV_PIX_FMT_QSV; } @@ -218,8 +88,8 @@ static int get_format(AVCodecContext *avctx, const enum AVPixelFormat *pix_fmts) } static int decode_packet(DecodeContext *decode, AVCodecContext *decoder_ctx, - AVFrame *frame, AVPacket *pkt, - AVIOContext *output_ctx) + AVFrame *frame, AVFrame *sw_frame, + AVPacket *pkt, AVIOContext *output_ctx) { int ret = 0; int got_frame = 1; @@ -238,61 +108,20 @@ static int decode_packet(DecodeContext *decode, AVCodecContext *decoder_ctx, * We just retrieve the raw data and write it to a file, which is rather * useless but pedagogic. */ if (got_frame) { - mfxFrameSurface1 *surf = (mfxFrameSurface1*)frame->data[3]; - VASurfaceID surface = *(VASurfaceID*)surf->Data.MemId; - - VAImageFormat img_fmt = { - .fourcc = VA_FOURCC_NV12, - .byte_order = VA_LSB_FIRST, - .bits_per_pixel = 8, - .depth = 8, - }; - - VAImage img; - - VAStatus err; - uint8_t *data; int i, j; - img.buf = VA_INVALID_ID; - img.image_id = VA_INVALID_ID; - - err = vaCreateImage(decode->va_dpy, &img_fmt, - frame->width, frame->height, &img); - if (err != VA_STATUS_SUCCESS) { - fprintf(stderr, "Error creating an image: %s\n", - vaErrorStr(err)); - ret = AVERROR_UNKNOWN; + ret = av_hwframe_transfer_data(sw_frame, frame, 0); + if (ret < 0) { + fprintf(stderr, "Error transferring the data to system memory\n"); goto fail; } - err = vaGetImage(decode->va_dpy, surface, 0, 0, - frame->width, frame->height, - img.image_id); - if (err != VA_STATUS_SUCCESS) { - fprintf(stderr, "Error getting an image: %s\n", - vaErrorStr(err)); - ret = AVERROR_UNKNOWN; - goto fail; - } - - err = vaMapBuffer(decode->va_dpy, img.buf, (void**)&data); - if (err != VA_STATUS_SUCCESS) { - fprintf(stderr, "Error mapping the image buffer: %s\n", - vaErrorStr(err)); - ret = AVERROR_UNKNOWN; - goto fail; - } - - for (i = 0; i < img.num_planes; i++) - for (j = 0; j < (img.height >> (i > 0)); j++) - avio_write(output_ctx, data + img.offsets[i] + j * img.pitches[i], img.width); + for (i = 0; i < FF_ARRAY_ELEMS(sw_frame->data) && sw_frame->data[i]; i++) + for (j = 0; j < (sw_frame->height >> (i > 0)); j++) + avio_write(output_ctx, sw_frame->data[i] + j * sw_frame->linesize[i], sw_frame->width); fail: - if (img.buf != VA_INVALID_ID) - vaUnmapBuffer(decode->va_dpy, img.buf); - if (img.image_id != VA_INVALID_ID) - vaDestroyImage(decode->va_dpy, img.image_id); + av_frame_unref(sw_frame); av_frame_unref(frame); if (ret < 0) @@ -311,28 +140,13 @@ int main(int argc, char **argv) const AVCodec *decoder; AVPacket pkt = { 0 }; - AVFrame *frame = NULL; + AVFrame *frame = NULL, *sw_frame = NULL; DecodeContext decode = { NULL }; - Display *dpy = NULL; - int va_ver_major, va_ver_minor; - - mfxIMPL mfx_impl = MFX_IMPL_AUTO_ANY; - mfxVersion mfx_ver = { { 1, 1 } }; - - mfxFrameAllocator frame_allocator = { - .pthis = &decode, - .Alloc = frame_alloc, - .Lock = frame_lock, - .Unlock = frame_unlock, - .GetHDL = frame_get_hdl, - .Free = frame_free, - }; - AVIOContext *output_ctx = NULL; - int ret, i, err; + int ret, i; av_register_all(); @@ -362,34 +176,13 @@ int main(int argc, char **argv) goto finish; } - /* initialize VA-API */ - dpy = XOpenDisplay(NULL); - if (!dpy) { - fprintf(stderr, "Cannot open the X display\n"); + /* open the hardware device */ + ret = av_hwdevice_ctx_create(&decode.hw_device_ref, AV_HWDEVICE_TYPE_QSV, + "auto", NULL, 0); + if (ret < 0) { + fprintf(stderr, "Cannot open the hardware device\n"); goto finish; } - decode.va_dpy = vaGetDisplay(dpy); - if (!decode.va_dpy) { - fprintf(stderr, "Cannot open the VA display\n"); - goto finish; - } - - err = vaInitialize(decode.va_dpy, &va_ver_major, &va_ver_minor); - if (err != VA_STATUS_SUCCESS) { - fprintf(stderr, "Cannot initialize VA: %s\n", vaErrorStr(err)); - goto finish; - } - fprintf(stderr, "Initialized VA v%d.%d\n", va_ver_major, va_ver_minor); - - /* initialize an MFX session */ - err = MFXInit(mfx_impl, &mfx_ver, &decode.mfx_session); - if (err != MFX_ERR_NONE) { - fprintf(stderr, "Error initializing an MFX session\n"); - goto finish; - } - - MFXVideoCORE_SetHandle(decode.mfx_session, MFX_HANDLE_VA_DISPLAY, decode.va_dpy); - MFXVideoCORE_SetFrameAllocator(decode.mfx_session, &frame_allocator); /* initialize the decoder */ decoder = avcodec_find_decoder_by_name("h264_qsv"); @@ -418,7 +211,6 @@ int main(int argc, char **argv) decoder_ctx->refcounted_frames = 1; decoder_ctx->opaque = &decode; - decoder_ctx->get_buffer2 = get_buffer; decoder_ctx->get_format = get_format; ret = avcodec_open2(decoder_ctx, NULL, NULL); @@ -434,8 +226,9 @@ int main(int argc, char **argv) goto finish; } - frame = av_frame_alloc(); - if (!frame) { + frame = av_frame_alloc(); + sw_frame = av_frame_alloc(); + if (!frame || !sw_frame) { ret = AVERROR(ENOMEM); goto finish; } @@ -447,7 +240,7 @@ int main(int argc, char **argv) break; if (pkt.stream_index == video_st->index) - ret = decode_packet(&decode, decoder_ctx, frame, &pkt, output_ctx); + ret = decode_packet(&decode, decoder_ctx, frame, sw_frame, &pkt, output_ctx); av_packet_unref(&pkt); } @@ -455,7 +248,7 @@ int main(int argc, char **argv) /* flush the decoder */ pkt.data = NULL; pkt.size = 0; - ret = decode_packet(&decode, decoder_ctx, frame, &pkt, output_ctx); + ret = decode_packet(&decode, decoder_ctx, frame, sw_frame, &pkt, output_ctx); finish: if (ret < 0) { @@ -467,19 +260,11 @@ finish: avformat_close_input(&input_ctx); av_frame_free(&frame); + av_frame_free(&sw_frame); - if (decoder_ctx) - av_freep(&decoder_ctx->hwaccel_context); avcodec_free_context(&decoder_ctx); - free_surfaces(&decode); - - if (decode.mfx_session) - MFXClose(decode.mfx_session); - if (decode.va_dpy) - vaTerminate(decode.va_dpy); - if (dpy) - XCloseDisplay(dpy); + av_buffer_unref(&decode.hw_device_ref); avio_close(output_ctx);