[FFmpeg-devel,2/2] libavdevice: Add SQV screen capture device

Submitted by Alexander Bilyak on Aug. 11, 2017, 9:10 a.m.

Details

Message ID 20170811091046.1094-2-bilyak.alexander@gmail.com
State New
Headers show

Commit Message

Alexander Bilyak Aug. 11, 2017, 9:10 a.m.
For making it more reasonable QSV screen capture is added to
device list, so it can be read as usual device, producing
packets to its output. Nevertheless it is only wrapper around QSV decoder
with screen capture plugin loading
---
 configure                |   2 +
 libavdevice/Makefile     |   1 +
 libavdevice/alldevices.c |   1 +
 libavdevice/qsvscreen.c  | 216 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 220 insertions(+)
 create mode 100644 libavdevice/qsvscreen.c

Patch hide | download patch | download mbox

diff --git a/configure b/configure
index 942765e8a3..c5119cf672 100755
--- a/configure
+++ b/configure
@@ -3035,6 +3035,8 @@  oss_indev_deps_any="soundcard_h sys_soundcard_h"
 oss_outdev_deps_any="soundcard_h sys_soundcard_h"
 pulse_indev_deps="libpulse"
 pulse_outdev_deps="libpulse"
+screen_qsv_indev_deps="libmfx"
+screen_qsv_indev_select="screen_qsv_decoder"
 qtkit_indev_extralibs="-framework QTKit -framework Foundation -framework QuartzCore"
 qtkit_indev_select="qtkit"
 sdl2_outdev_deps="sdl2"
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index c055d6718d..327cbb0a6c 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -38,6 +38,7 @@  OBJS-$(CONFIG_PULSE_INDEV)               += pulse_audio_dec.o \
                                             pulse_audio_common.o timefilter.o
 OBJS-$(CONFIG_PULSE_OUTDEV)              += pulse_audio_enc.o \
                                             pulse_audio_common.o
+OBJS-$(CONFIG_SCREEN_QSV_INDEV)          += qsvscreen.o
 OBJS-$(CONFIG_QTKIT_INDEV)               += qtkit.o
 OBJS-$(CONFIG_SDL2_OUTDEV)               += sdl2.o
 OBJS-$(CONFIG_SNDIO_INDEV)               += sndio_dec.o sndio.o
diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
index a8ed53ae5d..bcc46c2691 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -57,6 +57,7 @@  static void register_all(void)
     REGISTER_OUTDEV  (OPENGL,           opengl);
     REGISTER_INOUTDEV(OSS,              oss);
     REGISTER_INOUTDEV(PULSE,            pulse);
+	REGISTER_INDEV   (SCREEN_QSV,       screen_qsv);
     REGISTER_INDEV   (QTKIT,            qtkit);
     REGISTER_OUTDEV  (SDL2,             sdl2);
     REGISTER_INOUTDEV(SNDIO,            sndio);
diff --git a/libavdevice/qsvscreen.c b/libavdevice/qsvscreen.c
new file mode 100644
index 0000000000..475a9a4c50
--- /dev/null
+++ b/libavdevice/qsvscreen.c
@@ -0,0 +1,216 @@ 
+/*
+ * Intel QSV screen capture interface
+ *
+ * This file is part of FFmpeg.
+ *
+ * Copyright (C) 2017 Alexander Bilyak <bilyak.alexander@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Intel QSV Screen capture demuxer interface
+ * @author Alexander Bilyak <bilyak.alexander@gmail.com>
+ * @note As Intel Media SDK provide screen capture as plug-in to QSV decoder
+ * so this "device" is nothing more than wrapper around QSV decoder
+ * with appropiate settings.
+ */
+
+#include "config.h"
+#include "libavformat/internal.h"
+#include "libavutil/opt.h"
+#include "libavcodec/avcodec.h"
+#include "libavutil/time.h"
+#include "libavutil/imgutils.h"
+
+/**
+ * QSV screen capture context
+ */
+typedef struct QSVScreenContext {
+
+    const AVClass   *class;     /**< Class for private options */
+
+    AVCodecContext  *avctx;     /**< Codec context used for fake decoding */
+    AVCodec         *codec;     /**< Codec used for fake decoding */
+
+    int             draw_mouse; /**< Draw mouse cursor (private option) */
+    int             width;      /**< Width of the grab frame (private option) */
+    int             height;     /**< Height of the grab frame (private option) */
+} QSVScreenContext;
+
+/**
+ * Initializes the QSV screen device demuxer (public device demuxer API).
+ *
+ * @param avctx Context from avformat core
+ *
+ * @return 0 on success, a negative AVERROR on error
+ */
+static int screen_qsv_read_header(AVFormatContext *avctx)
+{
+    int ret;
+    AVStream *st;
+    QSVScreenContext *ctx = avctx->priv_data;
+    AVDictionary* opts = NULL;
+
+    ctx->codec = avcodec_find_decoder_by_name("screen_qsv");
+    if (!ctx->codec) {
+        ret = AVERROR_DECODER_NOT_FOUND;
+        goto error;
+    }
+
+    ctx->avctx = avcodec_alloc_context3(ctx->codec);
+    if (!ctx->avctx) {
+        ret = AVERROR(ENOMEM);
+        goto error;
+    }
+
+    ctx->avctx->width = ctx->width;
+    ctx->avctx->height = ctx->height;
+
+    av_dict_set_int(&opts, "draw_mouse", ctx->draw_mouse, 0 );
+
+    ret = avcodec_open2(ctx->avctx, ctx->codec, &opts);
+    if (ret)
+        goto error;
+
+    st = avformat_new_stream(avctx, NULL);
+    if (!st) {
+        ret = AVERROR(ENOMEM);
+        goto error;
+    }
+    avpriv_set_pts_info(st, 96, 1, 1000000); /* 96 bits pts in microseconds */
+
+    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+    st->codecpar->codec_id   = AV_CODEC_ID_RAWVIDEO;
+    st->codecpar->codec_tag  = MKTAG('N', 'V', '1', '2');
+    st->codecpar->width      = ctx->width;
+    st->codecpar->height     = ctx->height;
+    st->codecpar->format     = AV_PIX_FMT_NV12;
+    st->codecpar->bit_rate   = st->codecpar->width * st->codecpar->height * 12;
+
+    ret = avcodec_send_packet(ctx->avctx, NULL);
+    if (ret)
+        goto error;
+
+    return 0;
+
+error:
+    if (ctx->avctx)
+        avcodec_free_context(&ctx->avctx);
+
+    return ret;
+}
+
+/**
+ * Grabs a frame from QSV screen device (public device demuxer API).
+ *
+ * @param avctx Context from avformat core
+ *
+ * @param pkt Packet holding the grabbed frame
+ *
+ * @return frame size in bytes, a negative AVERROR on error.
+ */
+static int screen_qsv_read_packet(AVFormatContext *avctx, AVPacket *pkt)
+{
+    QSVScreenContext *ctx = avctx->priv_data;
+    int ret;
+
+    int size = ctx->width * ctx->height * 12/8;
+
+    AVFrame *frame = av_frame_alloc();
+    if (!frame)
+        return AVERROR(ENOMEM);
+
+    frame->format = AV_PIX_FMT_NV12;
+    frame->width = ctx->width;
+    frame->height = ctx->height;
+
+    ret = av_frame_get_buffer(frame, 32);
+    if (ret < 0)
+        goto error;
+
+    ret = avcodec_receive_frame(ctx->avctx, frame);
+    if (ret < 0)
+        goto error;
+
+    ret = av_new_packet(pkt, size);
+    if (ret < 0)
+        goto error;
+
+    ret = av_image_copy_to_buffer(pkt->data, size, (const uint8_t **)frame->data,
+                frame->linesize, frame->format, frame->width, frame->height, 1);
+    if (ret < 0)
+        goto error;
+
+    pkt->stream_index = 0;
+    pkt->pts = av_gettime();
+    pkt->pos = frame->pkt_pos;
+    pkt->size = size;
+
+    av_frame_free(&frame);
+
+    return size;
+error:
+    if (frame) {
+        av_frame_free(&frame);
+    }
+
+    return ret;
+}
+
+/**
+ * Closes QSV screen device frame grabber (public device demuxer API).
+ *
+ * @param avctx Context from avformat core
+ *
+ * @return 0 on success, a negative AVERROR on error
+ */
+static int screen_qsv_read_close(AVFormatContext *avctx)
+{
+    QSVScreenContext *ctx = avctx->priv_data;
+
+    if (ctx->avctx)
+        avcodec_free_context(&ctx->avctx);
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(struct QSVScreenContext, x)
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+    { "draw_mouse", "draw the mouse pointer", OFFSET(draw_mouse), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
+    { "video_size", "set video frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC },
+    { NULL },
+};
+
+static const AVClass screen_qsv_class = {
+    .class_name = "QSVScreen indev",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+/** QSV Screen frame grabber device demuxer declaration */
+AVInputFormat ff_screen_qsv_demuxer = {
+    .name           = "screen_qsv",
+    .long_name      = NULL_IF_CONFIG_SMALL("Intel QSV screen capture"),
+    .priv_data_size = sizeof(QSVScreenContext),
+    .read_header    = screen_qsv_read_header,
+    .read_packet    = screen_qsv_read_packet,
+    .read_close     = screen_qsv_read_close,
+    .flags          = AVFMT_NOFILE,
+    .priv_class     = &screen_qsv_class,
+};