libavcodec/qsvdec_h2645.c
4e08c821
 /*
41d47ea8
  * Intel MediaSDK QSV based H.264 / HEVC decoder
4e08c821
  *
  * copyright (c) 2013 Luca Barbato
  * copyright (c) 2015 Anton Khirnov
  *
508b79e6
  * This file is part of FFmpeg.
4e08c821
  *
508b79e6
  * FFmpeg is free software; you can redistribute it and/or
4e08c821
  * 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.
  *
508b79e6
  * FFmpeg is distributed in the hope that it will be useful,
4e08c821
  * 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
508b79e6
  * License along with FFmpeg; if not, write to the Free Software
4e08c821
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 
 #include <stdint.h>
 #include <string.h>
 
 #include <mfx/mfxvideo.h>
 
 #include "libavutil/common.h"
 #include "libavutil/fifo.h"
 #include "libavutil/opt.h"
 
 #include "avcodec.h"
 #include "internal.h"
1f26a231
 #include "qsv_internal.h"
b04d009b
 #include "qsvdec.h"
1f26a231
 #include "qsv.h"
4e08c821
 
41d47ea8
 enum LoadPlugin {
     LOAD_PLUGIN_NONE,
     LOAD_PLUGIN_HEVC_SW,
1f26a231
     LOAD_PLUGIN_HEVC_HW,
41d47ea8
 };
 
 typedef struct QSVH2645Context {
4e08c821
     AVClass *class;
     QSVContext qsv;
 
41d47ea8
     int load_plugin;
 
b93e2233
     AVFifoBuffer *packet_fifo;
 
8fb4210a
     AVPacket buffer_pkt;
41d47ea8
 } QSVH2645Context;
4e08c821
 
b93e2233
 static void qsv_clear_buffers(QSVH2645Context *s)
 {
     AVPacket pkt;
     while (av_fifo_size(s->packet_fifo) >= sizeof(pkt)) {
         av_fifo_generic_read(s->packet_fifo, &pkt, sizeof(pkt), NULL);
         av_packet_unref(&pkt);
     }
 
8fb4210a
     av_packet_unref(&s->buffer_pkt);
b93e2233
 }
 
4e08c821
 static av_cold int qsv_decode_close(AVCodecContext *avctx)
 {
41d47ea8
     QSVH2645Context *s = avctx->priv_data;
4e08c821
 
9ba27c23
     ff_qsv_decode_close(&s->qsv);
4e08c821
 
b93e2233
     qsv_clear_buffers(s);
 
     av_fifo_free(s->packet_fifo);
4e08c821
 
     return 0;
 }
 
 static av_cold int qsv_decode_init(AVCodecContext *avctx)
 {
41d47ea8
     QSVH2645Context *s = avctx->priv_data;
4e08c821
     int ret;
 
41d47ea8
     if (avctx->codec_id == AV_CODEC_ID_HEVC && s->load_plugin != LOAD_PLUGIN_NONE) {
fd9212f2
         static const char * const uid_hevcdec_sw = "15dd936825ad475ea34e35f3f54217a6";
         static const char * const uid_hevcdec_hw = "33a61c0b4c27454ca8d85dde757c6f8e";
41d47ea8
 
         if (s->qsv.load_plugins[0]) {
             av_log(avctx, AV_LOG_WARNING,
                    "load_plugins is not empty, but load_plugin is not set to 'none'."
                    "The load_plugin value will be ignored.\n");
         } else {
             av_freep(&s->qsv.load_plugins);
1f26a231
 
             if (s->load_plugin == LOAD_PLUGIN_HEVC_SW)
                 s->qsv.load_plugins = av_strdup(uid_hevcdec_sw);
             else
                 s->qsv.load_plugins = av_strdup(uid_hevcdec_hw);
41d47ea8
             if (!s->qsv.load_plugins)
                 return AVERROR(ENOMEM);
         }
     }
1f26a231
 
b93e2233
     s->packet_fifo = av_fifo_alloc(sizeof(AVPacket));
     if (!s->packet_fifo) {
         ret = AVERROR(ENOMEM);
         goto fail;
     }
4e08c821
 
     return 0;
 fail:
     qsv_decode_close(avctx);
     return ret;
 }
 
 static int qsv_decode_frame(AVCodecContext *avctx, void *data,
                             int *got_frame, AVPacket *avpkt)
 {
41d47ea8
     QSVH2645Context *s = avctx->priv_data;
4e08c821
     AVFrame *frame    = data;
     int ret;
 
b93e2233
     /* buffer the input packet */
4e08c821
     if (avpkt->size) {
b93e2233
         AVPacket input_ref = { 0 };
4e08c821
 
b93e2233
         if (av_fifo_space(s->packet_fifo) < sizeof(input_ref)) {
             ret = av_fifo_realloc2(s->packet_fifo,
                                    av_fifo_size(s->packet_fifo) + sizeof(input_ref));
             if (ret < 0)
                 return ret;
         }
 
         ret = av_packet_ref(&input_ref, avpkt);
         if (ret < 0)
             return ret;
         av_fifo_generic_write(s->packet_fifo, &input_ref, sizeof(input_ref), NULL);
     }
 
     /* process buffered data */
     while (!*got_frame) {
8fb4210a
         /* prepare the input data */
         if (s->buffer_pkt.size <= 0) {
b93e2233
             /* no more data */
             if (av_fifo_size(s->packet_fifo) < sizeof(AVPacket))
1f26a231
                 return avpkt->size ? avpkt->size : ff_qsv_process_data(avctx, &s->qsv, frame, got_frame, avpkt);
b93e2233
 
8fb4210a
             av_packet_unref(&s->buffer_pkt);
b93e2233
 
8fb4210a
             av_fifo_generic_read(s->packet_fifo, &s->buffer_pkt, sizeof(s->buffer_pkt), NULL);
4e08c821
         }
b93e2233
 
8fb4210a
         ret = ff_qsv_process_data(avctx, &s->qsv, frame, got_frame, &s->buffer_pkt);
b93e2233
         if (ret < 0)
             return ret;
 
8fb4210a
         s->buffer_pkt.size -= ret;
         s->buffer_pkt.data += ret;
4e08c821
     }
 
b93e2233
     return avpkt->size;
4e08c821
 }
 
 static void qsv_decode_flush(AVCodecContext *avctx)
 {
3f8e2e99
     QSVH2645Context *s = avctx->priv_data;
1f26a231
 
     qsv_clear_buffers(s);
     ff_qsv_decode_flush(avctx, &s->qsv);
4e08c821
 }
 
136e7cf6
 #if defined(_WIN32)
 #define LOAD_PLUGIN_DEFAULT LOAD_PLUGIN_HEVC_SW
 #else
 #define LOAD_PLUGIN_DEFAULT LOAD_PLUGIN_HEVC_HW
 #endif
 
41d47ea8
 #define OFFSET(x) offsetof(QSVH2645Context, x)
 #define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
 
624aa8ab
 #if CONFIG_HEVC_QSV_DECODER
41d47ea8
 static const AVOption hevc_options[] = {
     { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 0, INT_MAX, VD },
 
136e7cf6
     { "load_plugin", "A user plugin to load in an internal session", OFFSET(load_plugin), AV_OPT_TYPE_INT, { .i64 = LOAD_PLUGIN_DEFAULT }, LOAD_PLUGIN_NONE, LOAD_PLUGIN_HEVC_HW, VD, "load_plugin" },
41d47ea8
     { "none",     NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_NONE },    0, 0, VD, "load_plugin" },
     { "hevc_sw",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_SW }, 0, 0, VD, "load_plugin" },
1f26a231
     { "hevc_hw",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_HW }, 0, 0, VD, "load_plugin" },
41d47ea8
 
     { "load_plugins", "A :-separate list of hexadecimal plugin UIDs to load in an internal session",
         OFFSET(qsv.load_plugins), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, VD },
     { NULL },
 };
 
 static const AVClass hevc_class = {
     .class_name = "hevc_qsv",
     .item_name  = av_default_item_name,
     .option     = hevc_options,
     .version    = LIBAVUTIL_VERSION_INT,
 };
 
 AVCodec ff_hevc_qsv_decoder = {
     .name           = "hevc_qsv",
     .long_name      = NULL_IF_CONFIG_SMALL("HEVC (Intel Quick Sync Video acceleration)"),
     .priv_data_size = sizeof(QSVH2645Context),
     .type           = AVMEDIA_TYPE_VIDEO,
     .id             = AV_CODEC_ID_HEVC,
     .init           = qsv_decode_init,
     .decode         = qsv_decode_frame,
     .flush          = qsv_decode_flush,
     .close          = qsv_decode_close,
4e7a7a96
     .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING,
41d47ea8
     .priv_class     = &hevc_class,
741b352b
     .pix_fmts       = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12,
92736c74
                                                     AV_PIX_FMT_P010,
741b352b
                                                     AV_PIX_FMT_QSV,
                                                     AV_PIX_FMT_NONE },
758fbc54
     .hw_configs     = ff_qsv_hw_configs,
8fb4210a
     .bsfs           = "hevc_mp4toannexb",
41d47ea8
 };
 #endif
 
624aa8ab
 #if CONFIG_H264_QSV_DECODER
4e08c821
 static const AVOption options[] = {
     { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 0, INT_MAX, VD },
     { NULL },
 };
 
 static const AVClass class = {
     .class_name = "h264_qsv",
     .item_name  = av_default_item_name,
     .option     = options,
     .version    = LIBAVUTIL_VERSION_INT,
 };
 
 AVCodec ff_h264_qsv_decoder = {
     .name           = "h264_qsv",
     .long_name      = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Intel Quick Sync Video acceleration)"),
41d47ea8
     .priv_data_size = sizeof(QSVH2645Context),
4e08c821
     .type           = AVMEDIA_TYPE_VIDEO,
     .id             = AV_CODEC_ID_H264,
     .init           = qsv_decode_init,
     .decode         = qsv_decode_frame,
     .flush          = qsv_decode_flush,
     .close          = qsv_decode_close,
4e7a7a96
     .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING,
4e08c821
     .priv_class     = &class,
741b352b
     .pix_fmts       = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12,
92736c74
                                                     AV_PIX_FMT_P010,
741b352b
                                                     AV_PIX_FMT_QSV,
                                                     AV_PIX_FMT_NONE },
758fbc54
     .hw_configs     = ff_qsv_hw_configs,
8fb4210a
     .bsfs           = "h264_mp4toannexb",
4e08c821
 };
41d47ea8
 #endif