diff --git a/projects/Rockchip/patches/ffmpeg/ffmpeg-0002-WIP-deint-filter.patch b/projects/Rockchip/patches/ffmpeg/ffmpeg-0002-WIP-deint-filter.patch new file mode 100644 index 0000000000..34ccc5b6e0 --- /dev/null +++ b/projects/Rockchip/patches/ffmpeg/ffmpeg-0002-WIP-deint-filter.patch @@ -0,0 +1,924 @@ +From 39069d9cc03a42cd497dd6b9756116ff4b684a5d Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Tue, 3 Dec 2019 21:01:18 +0100 +Subject: [PATCH] WIP deint filter + +--- + libavfilter/Makefile | 1 + + libavfilter/allfilters.c | 1 + + libavfilter/vf_deinterlace_v4l2m2m.c | 879 +++++++++++++++++++++++++++ + 3 files changed, 881 insertions(+) + create mode 100644 libavfilter/vf_deinterlace_v4l2m2m.c + +diff --git a/libavfilter/Makefile b/libavfilter/Makefile +index 512354065305..625fd29f9313 100644 +--- a/libavfilter/Makefile ++++ b/libavfilter/Makefile +@@ -218,6 +218,7 @@ OBJS-$(CONFIG_DEFLATE_FILTER) += vf_neighbor.o + OBJS-$(CONFIG_DEFLICKER_FILTER) += vf_deflicker.o + OBJS-$(CONFIG_DEINTERLACE_QSV_FILTER) += vf_deinterlace_qsv.o + OBJS-$(CONFIG_DEINTERLACE_VAAPI_FILTER) += vf_deinterlace_vaapi.o vaapi_vpp.o ++OBJS-$(CONFIG_DEINTERLACE_V4L2M2M_FILTER) += vf_deinterlace_v4l2m2m.o + OBJS-$(CONFIG_DEJUDDER_FILTER) += vf_dejudder.o + OBJS-$(CONFIG_DELOGO_FILTER) += vf_delogo.o + OBJS-$(CONFIG_DENOISE_VAAPI_FILTER) += vf_misc_vaapi.o vaapi_vpp.o +diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c +index 1183e4026751..fe5a2e8c02e8 100644 +--- a/libavfilter/allfilters.c ++++ b/libavfilter/allfilters.c +@@ -204,6 +204,7 @@ extern AVFilter ff_vf_dedot; + extern AVFilter ff_vf_deflate; + extern AVFilter ff_vf_deflicker; + extern AVFilter ff_vf_deinterlace_qsv; ++extern AVFilter ff_vf_deinterlace_v4l2m2m; + extern AVFilter ff_vf_deinterlace_vaapi; + extern AVFilter ff_vf_dejudder; + extern AVFilter ff_vf_delogo; +diff --git a/libavfilter/vf_deinterlace_v4l2m2m.c b/libavfilter/vf_deinterlace_v4l2m2m.c +new file mode 100644 +index 000000000000..1029e5b620fd +--- /dev/null ++++ b/libavfilter/vf_deinterlace_v4l2m2m.c +@@ -0,0 +1,879 @@ ++/* ++ * 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 ++ */ ++ ++/** ++ * @file ++ * deinterlace video filter - V4L2 M2M ++ */ ++ ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "libavutil/avassert.h" ++#include "libavutil/avstring.h" ++#include "libavutil/common.h" ++#include "libavutil/hwcontext.h" ++#include "libavutil/hwcontext_drm.h" ++#include "libavutil/internal.h" ++#include "libavutil/mathematics.h" ++#include "libavutil/opt.h" ++#include "libavutil/pixdesc.h" ++#include "libavutil/time.h" ++ ++#include "avfilter.h" ++#include "formats.h" ++#include "internal.h" ++#include "video.h" ++ ++typedef struct V4L2Queue V4L2Queue; ++typedef struct DeintV4L2M2MContextShared DeintV4L2M2MContextShared; ++ ++typedef struct V4L2PlaneInfo { ++ int bytesperline; ++ size_t length; ++} V4L2PlaneInfo; ++ ++typedef struct V4L2Buffer { ++ int enqueued; ++ int reenqueue; ++ int fd; ++ struct v4l2_buffer buffer; ++ struct v4l2_plane planes[VIDEO_MAX_PLANES]; ++ int num_planes; ++ V4L2PlaneInfo plane_info[VIDEO_MAX_PLANES]; ++ AVDRMFrameDescriptor drm_frame; ++ V4L2Queue *q; ++} V4L2Buffer; ++ ++typedef struct V4L2Queue { ++ struct v4l2_format format; ++ int num_buffers; ++ V4L2Buffer *buffers; ++ DeintV4L2M2MContextShared *ctx; ++} V4L2Queue; ++ ++typedef struct DeintV4L2M2MContextShared { ++ int fd; ++ int done; ++ int width; ++ int height; ++ int orig_width; ++ int orig_height; ++ atomic_uint refcount; ++ ++ AVBufferRef *hw_frames_ctx; ++ ++ int frame_count; ++ AVFrame *frames[2]; ++ ++ V4L2Queue output; ++ V4L2Queue capture; ++} DeintV4L2M2MContextShared; ++ ++typedef struct DeintV4L2M2MContext { ++ const AVClass *class; ++ ++ DeintV4L2M2MContextShared *shared; ++} DeintV4L2M2MContext; ++ ++static int deint_v4l2m2m_prepare_context(DeintV4L2M2MContextShared *ctx) ++{ ++ struct v4l2_capability cap; ++ int ret; ++ ++ memset(&cap, 0, sizeof(cap)); ++ ret = ioctl(ctx->fd, VIDIOC_QUERYCAP, &cap); ++ if (ret < 0) ++ return ret; ++ ++ if (!(cap.capabilities & V4L2_CAP_STREAMING)) ++ return AVERROR(EINVAL); ++ ++ if (cap.capabilities & V4L2_CAP_VIDEO_M2M) { ++ ctx->capture.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ctx->output.format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ ++ return 0; ++ } ++ ++ if (cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) { ++ ctx->capture.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ++ ctx->output.format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; ++ ++ return 0; ++ } ++ ++ return AVERROR(EINVAL); ++} ++ ++static int deint_v4l2m2m_try_format(V4L2Queue *queue) ++{ ++ struct v4l2_format *fmt = &queue->format; ++ DeintV4L2M2MContextShared *ctx = queue->ctx; ++ int ret, field; ++ ++ ret = ioctl(ctx->fd, VIDIOC_G_FMT, fmt); ++ if (ret) ++ av_log(NULL, AV_LOG_ERROR, "VIDIOC_G_FMT failed: %d\n", ret); ++ ++ if (V4L2_TYPE_IS_OUTPUT(fmt->type)) ++ field = V4L2_FIELD_INTERLACED_TB; ++ else ++ field = V4L2_FIELD_NONE; ++ ++ if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { ++ fmt->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; ++ fmt->fmt.pix_mp.field = field; ++ fmt->fmt.pix_mp.width = ctx->width; ++ fmt->fmt.pix_mp.height = ctx->height; ++ } else { ++ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; ++ fmt->fmt.pix.field = field; ++ fmt->fmt.pix.width = ctx->width; ++ fmt->fmt.pix.height = ctx->height; ++ } ++ ++ ret = ioctl(ctx->fd, VIDIOC_TRY_FMT, fmt); ++ if (ret) ++ return AVERROR(EINVAL); ++ ++ if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { ++ if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12 || ++ fmt->fmt.pix_mp.field != field) { ++ av_log(NULL, AV_LOG_DEBUG, "format not supported for type %d\n", fmt->type); ++ ++ return AVERROR(EINVAL); ++ } ++ } else { ++ if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_NV12 || ++ fmt->fmt.pix.field != field) { ++ av_log(NULL, AV_LOG_DEBUG, "format not supported for type %d\n", fmt->type); ++ ++ return AVERROR(EINVAL); ++ } ++ } ++ ++ return 0; ++} ++ ++static int deint_v4l2m2m_set_format(V4L2Queue *queue, uint32_t field, int width, int height) ++{ ++ struct v4l2_format *fmt = &queue->format; ++ DeintV4L2M2MContextShared *ctx = queue->ctx; ++ int ret; ++ ++ if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { ++ fmt->fmt.pix_mp.field = field; ++ fmt->fmt.pix_mp.width = width; ++ fmt->fmt.pix_mp.height = height; ++ /* TODO: bytesperline and imagesize */ ++ } else { ++ fmt->fmt.pix.field = field; ++ fmt->fmt.pix.width = width; ++ fmt->fmt.pix.height = height; ++ fmt->fmt.pix.sizeimage = 0; ++ fmt->fmt.pix.bytesperline = 0; ++ } ++ ++ ret = ioctl(ctx->fd, VIDIOC_S_FMT, fmt); ++ if (ret) ++ av_log(NULL, AV_LOG_ERROR, "VIDIOC_S_FMT failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int deint_v4l2m2m_probe_device(DeintV4L2M2MContextShared *ctx, char *node) ++{ ++ int ret; ++ ++ ctx->fd = open(node, O_RDWR | O_NONBLOCK, 0); ++ if (ctx->fd < 0) ++ return AVERROR(errno); ++ ++ ret = deint_v4l2m2m_prepare_context(ctx); ++ if (ret) ++ goto fail; ++ ++ ret = deint_v4l2m2m_try_format(&ctx->capture); ++ if (ret) ++ goto fail; ++ ++ ret = deint_v4l2m2m_try_format(&ctx->output); ++ if (ret) ++ goto fail; ++ ++ return 0; ++ ++fail: ++ close(ctx->fd); ++ ctx->fd = -1; ++ ++ return ret; ++} ++ ++static int deint_v4l2m2m_find_device(DeintV4L2M2MContextShared *ctx) ++{ ++ int ret = AVERROR(EINVAL); ++ struct dirent *entry; ++ char node[PATH_MAX]; ++ DIR *dirp; ++ ++ dirp = opendir("/dev"); ++ if (!dirp) ++ return AVERROR(errno); ++ ++ for (entry = readdir(dirp); entry; entry = readdir(dirp)) { ++ ++ if (strncmp(entry->d_name, "video", 5)) ++ continue; ++ ++ snprintf(node, sizeof(node), "/dev/%s", entry->d_name); ++ av_log(NULL, AV_LOG_DEBUG, "probing device %s\n", node); ++ ret = deint_v4l2m2m_probe_device(ctx, node); ++ if (!ret) ++ break; ++ } ++ ++ closedir(dirp); ++ ++ if (ret) { ++ av_log(NULL, AV_LOG_ERROR, "Could not find a valid device\n"); ++ ctx->fd = -1; ++ ++ return ret; ++ } ++ ++ av_log(NULL, AV_LOG_INFO, "Using device %s\n", node); ++ ++ return 0; ++} ++ ++static int deint_v4l2m2m_enqueue_buffer(V4L2Buffer *buf) ++{ ++ int ret; ++ ++ ret = ioctl(buf->q->ctx->fd, VIDIOC_QBUF, &buf->buffer); ++ if (ret < 0) ++ return AVERROR(errno); ++ ++ buf->enqueued = 1; ++ ++ return 0; ++} ++ ++static int v4l2_buffer_export_drm(V4L2Buffer* avbuf) ++{ ++ struct v4l2_exportbuffer expbuf; ++ int i, ret; ++ ++ for (i = 0; i < avbuf->num_planes; i++) { ++ memset(&expbuf, 0, sizeof(expbuf)); ++ ++ expbuf.index = avbuf->buffer.index; ++ expbuf.type = avbuf->buffer.type; ++ expbuf.plane = i; ++ ++ ret = ioctl(avbuf->q->ctx->fd, VIDIOC_EXPBUF, &expbuf); ++ if (ret < 0) ++ return AVERROR(errno); ++ ++ avbuf->fd = expbuf.fd; ++ ++ if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->buffer.type)) { ++ /* drm frame */ ++ avbuf->drm_frame.objects[i].size = avbuf->buffer.m.planes[i].length; ++ avbuf->drm_frame.objects[i].fd = expbuf.fd; ++ avbuf->drm_frame.objects[i].format_modifier = DRM_FORMAT_MOD_LINEAR; ++ } else { ++ /* drm frame */ ++ avbuf->drm_frame.objects[0].size = avbuf->buffer.length; ++ avbuf->drm_frame.objects[0].fd = expbuf.fd; ++ avbuf->drm_frame.objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR; ++ } ++ } ++ ++ return 0; ++} ++ ++static int deint_v4l2m2m_allocate_buffers(V4L2Queue *queue) ++{ ++ struct v4l2_format *fmt = &queue->format; ++ DeintV4L2M2MContextShared *ctx = queue->ctx; ++ struct v4l2_requestbuffers req; ++ int ret, i, j, multiplanar; ++ uint32_t memory; ++ ++ memory = V4L2_TYPE_IS_OUTPUT(fmt->type) ? ++ V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP; ++ ++ multiplanar = V4L2_TYPE_IS_MULTIPLANAR(fmt->type); ++ ++ memset(&req, 0, sizeof(req)); ++ req.count = queue->num_buffers; ++ req.memory = memory; ++ req.type = fmt->type; ++ ++ ret = ioctl(ctx->fd, VIDIOC_REQBUFS, &req); ++ if (ret < 0) { ++ av_log(NULL, AV_LOG_ERROR, "VIDIOC_REQBUFS failed: %s\n", strerror(errno)); ++ ++ return AVERROR(errno); ++ } ++ ++ queue->num_buffers = req.count; ++ queue->buffers = av_mallocz(queue->num_buffers * sizeof(V4L2Buffer)); ++ if (!queue->buffers) { ++ av_log(NULL, AV_LOG_ERROR, "malloc enomem\n"); ++ ++ return AVERROR(ENOMEM); ++ } ++ ++ for (i = 0; i < queue->num_buffers; i++) { ++ V4L2Buffer *buf = &queue->buffers[i]; ++ ++ buf->enqueued = 0; ++ buf->fd = -1; ++ buf->q = queue; ++ ++ buf->buffer.type = fmt->type; ++ buf->buffer.memory = memory; ++ buf->buffer.index = i; ++ ++ if (multiplanar) { ++ buf->buffer.length = VIDEO_MAX_PLANES; ++ buf->buffer.m.planes = buf->planes; ++ } ++ ++ ret = ioctl(ctx->fd, VIDIOC_QUERYBUF, &buf->buffer); ++ if (ret < 0) { ++ ret = AVERROR(errno); ++ ++ goto fail; ++ } ++ ++ if (multiplanar) ++ buf->num_planes = buf->buffer.length; ++ else ++ buf->num_planes = 1; ++ ++ for (j = 0; j < buf->num_planes; j++) { ++ V4L2PlaneInfo *info = &buf->plane_info[j]; ++ ++ if (multiplanar) { ++ info->bytesperline = fmt->fmt.pix_mp.plane_fmt[j].bytesperline; ++ info->length = buf->buffer.m.planes[j].length; ++ } else { ++ info->bytesperline = fmt->fmt.pix.bytesperline; ++ info->length = buf->buffer.length; ++ } ++ } ++ ++ if (!V4L2_TYPE_IS_OUTPUT(fmt->type)) { ++ ret = deint_v4l2m2m_enqueue_buffer(buf); ++ if (ret) ++ goto fail; ++ ++ ret = v4l2_buffer_export_drm(buf); ++ if (ret) ++ goto fail; ++ } ++ } ++ ++ return 0; ++ ++fail: ++ for (i = 0; i < queue->num_buffers; i++) ++ if (queue->buffers[i].fd >= 0) ++ close(queue->buffers[i].fd); ++ av_free(queue->buffers); ++ queue->buffers = NULL; ++ ++ return ret; ++} ++ ++static int deint_v4l2m2m_streamon(V4L2Queue *queue) ++{ ++ int type = queue->format.type; ++ int ret; ++ ++ ret = ioctl(queue->ctx->fd, VIDIOC_STREAMON, &type); ++ if (ret < 0) ++ return AVERROR(errno); ++ ++ return 0; ++} ++ ++static int deint_v4l2m2m_streamoff(V4L2Queue *queue) ++{ ++ int type = queue->format.type; ++ int ret; ++ ++ ret = ioctl(queue->ctx->fd, VIDIOC_STREAMOFF, &type); ++ if (ret < 0) ++ return AVERROR(errno); ++ ++ return 0; ++} ++ ++static V4L2Buffer* deint_v4l2m2m_dequeue_buffer(V4L2Queue *queue, int timeout) ++{ ++ struct v4l2_plane planes[VIDEO_MAX_PLANES]; ++ DeintV4L2M2MContextShared *ctx = queue->ctx; ++ struct v4l2_buffer buf = { 0 }; ++ V4L2Buffer* avbuf = NULL; ++ struct pollfd pfd; ++ short events; ++ int ret; ++ ++ if (V4L2_TYPE_IS_OUTPUT(queue->format.type)) ++ events = POLLOUT | POLLWRNORM; ++ else ++ events = POLLIN | POLLRDNORM; ++ ++ pfd.events = events; ++ pfd.fd = ctx->fd; ++ ++ for (;;) { ++ ret = poll(&pfd, 1, timeout); ++ if (ret > 0) ++ break; ++ if (errno == EINTR) ++ continue; ++ return NULL; ++ } ++ ++ if (pfd.revents & POLLERR) ++ return NULL; ++ ++ if (pfd.revents & events) { ++ memset(&buf, 0, sizeof(buf)); ++ buf.memory = V4L2_MEMORY_MMAP; ++ buf.type = queue->format.type; ++ if (V4L2_TYPE_IS_MULTIPLANAR(queue->format.type)) { ++ memset(planes, 0, sizeof(planes)); ++ buf.length = VIDEO_MAX_PLANES; ++ buf.m.planes = planes; ++ } ++ ++ ret = ioctl(ctx->fd, VIDIOC_DQBUF, &buf); ++ if (ret) { ++ if (errno != EAGAIN) ++ av_log(NULL, AV_LOG_DEBUG, "VIDIOC_DQBUF, errno (%s)\n", ++ av_err2str(AVERROR(errno))); ++ return NULL; ++ } ++ ++ avbuf = &queue->buffers[buf.index]; ++ avbuf->enqueued = 0; ++ avbuf->buffer = buf; ++ if (V4L2_TYPE_IS_MULTIPLANAR(queue->format.type)) { ++ memcpy(avbuf->planes, planes, sizeof(planes)); ++ avbuf->buffer.m.planes = avbuf->planes; ++ } ++ ++ return avbuf; ++ } ++ ++ return NULL; ++} ++ ++static V4L2Buffer *deint_v4l2m2m_find_free_buf(V4L2Queue *queue) ++{ ++ int i; ++ ++ for (i = 0; i < queue->num_buffers; i++) ++ if (!queue->buffers[i].enqueued) ++ return &queue->buffers[i]; ++ ++ return NULL; ++} ++ ++static int deint_v4l2m2m_enqueue(V4L2Queue *queue, const AVFrame* frame) ++{ ++ AVDRMFrameDescriptor *drm_desc = (AVDRMFrameDescriptor *)frame->data[0]; ++ V4L2Buffer *buf; ++ int i; ++ ++ if (V4L2_TYPE_IS_OUTPUT(queue->format.type)) ++ while (deint_v4l2m2m_dequeue_buffer(queue, 0)); ++ ++ buf = deint_v4l2m2m_find_free_buf(queue); ++ if (!buf) ++ return AVERROR(ENOMEM); ++ ++ if (V4L2_TYPE_IS_MULTIPLANAR(buf->buffer.type)) ++ for (i = 0; i < drm_desc->nb_objects; i++) ++ buf->buffer.m.planes[i].m.fd = drm_desc->objects[i].fd; ++ else ++ buf->buffer.m.fd = drm_desc->objects[0].fd; ++ ++ return deint_v4l2m2m_enqueue_buffer(buf); ++} ++ ++static void deint_v4l2m2m_destroy_context(DeintV4L2M2MContextShared *ctx) ++{ ++ if (atomic_fetch_sub(&ctx->refcount, 1) == 1) { ++ V4L2Queue *capture = &ctx->capture; ++ V4L2Queue *output = &ctx->output; ++ int i; ++ ++ av_log(NULL, AV_LOG_DEBUG, "%s - destroying context\n", __func__); ++ ++ if (ctx->fd >= 0) { ++ deint_v4l2m2m_streamoff(capture); ++ deint_v4l2m2m_streamoff(output); ++ } ++ ++ if (capture->buffers) ++ for (i = 0; i < capture->num_buffers; i++) { ++ capture->buffers[i].q = NULL; ++ if (capture->buffers[i].fd >= 0) ++ close(capture->buffers[i].fd); ++ } ++ ++ for (i = 0; i < ctx->frame_count; i++) ++ av_frame_free(&ctx->frames[i]); ++ ++ av_buffer_unref(&ctx->hw_frames_ctx); ++ ++ if (capture->buffers) ++ av_free(capture->buffers); ++ ++ if (output->buffers) ++ av_free(output->buffers); ++ ++ if (ctx->fd >= 0) { ++ close(ctx->fd); ++ ctx->fd = -1; ++ } ++ ++ av_free(ctx); ++ } ++} ++ ++static void v4l2_free_buffer(void *opaque, uint8_t *unused) ++{ ++ V4L2Buffer *buf = opaque; ++ DeintV4L2M2MContextShared *ctx = buf->q->ctx; ++ ++ if (!ctx->done) ++ deint_v4l2m2m_enqueue_buffer(buf); ++ ++ deint_v4l2m2m_destroy_context(ctx); ++} ++ ++static uint8_t *v4l2_get_drm_frame(V4L2Buffer *avbuf, int height) ++{ ++ AVDRMFrameDescriptor *drm_desc = &avbuf->drm_frame; ++ AVDRMLayerDescriptor *layer; ++ ++ /* fill the DRM frame descriptor */ ++ drm_desc->nb_objects = avbuf->num_planes; ++ drm_desc->nb_layers = 1; ++ ++ layer = &drm_desc->layers[0]; ++ layer->nb_planes = avbuf->num_planes; ++ ++ for (int i = 0; i < avbuf->num_planes; i++) { ++ layer->planes[i].object_index = i; ++ layer->planes[i].offset = 0; ++ layer->planes[i].pitch = avbuf->plane_info[i].bytesperline; ++ } ++ ++ layer->format = DRM_FORMAT_NV12; ++ ++ if (avbuf->num_planes == 1) { ++ layer->nb_planes = 2; ++ ++ layer->planes[1].object_index = 0; ++ layer->planes[1].offset = avbuf->plane_info[0].bytesperline * height; ++ layer->planes[1].pitch = avbuf->plane_info[0].bytesperline; ++ } ++ ++ return (uint8_t *)drm_desc; ++} ++ ++static int deint_v4l2m2m_dequeue_frame(V4L2Queue *queue, AVFrame* frame, int timeout) ++{ ++ DeintV4L2M2MContextShared *ctx = queue->ctx; ++ V4L2Buffer* avbuf; ++ ++ avbuf = deint_v4l2m2m_dequeue_buffer(queue, timeout); ++ if (!avbuf) { ++ av_log(NULL, AV_LOG_ERROR, "dequeueing failed\n"); ++ return AVERROR(EINVAL); ++ } ++ ++ frame->buf[0] = av_buffer_create((uint8_t *) &avbuf->drm_frame, ++ sizeof(avbuf->drm_frame), v4l2_free_buffer, ++ avbuf, AV_BUFFER_FLAG_READONLY); ++ if (!frame->buf[0]) ++ return AVERROR(ENOMEM); ++ ++ atomic_fetch_add(&ctx->refcount, 1); ++ ++ frame->data[0] = (uint8_t *)v4l2_get_drm_frame(avbuf, ctx->orig_height); ++ frame->format = AV_PIX_FMT_DRM_PRIME; ++ frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frames_ctx); ++ frame->height = ctx->height; ++ frame->width = ctx->width; ++ ++ if (avbuf->buffer.flags & V4L2_BUF_FLAG_ERROR) { ++ av_log(NULL, AV_LOG_ERROR, "driver decode error\n"); ++ frame->decode_error_flags |= FF_DECODE_ERROR_INVALID_BITSTREAM; ++ } ++ ++ return 0; ++} ++ ++static int deint_v4l2m2m_dequeue(AVFilterContext *avctx, AVFrame *input_frame, int field) ++{ ++ DeintV4L2M2MContext *priv = avctx->priv; ++ DeintV4L2M2MContextShared *ctx = priv->shared; ++ AVFilterLink *outlink = avctx->outputs[0]; ++ AVFrame *output_frame; ++ int err; ++ ++ output_frame = av_frame_alloc(); ++ ++ if (!output_frame) ++ return AVERROR(ENOMEM); ++ ++ err = deint_v4l2m2m_dequeue_frame(&ctx->capture, output_frame, 500); ++ if (err < 0) { ++ av_log(priv, AV_LOG_ERROR, "no frame (field %d)\n", field); ++ goto fail; ++ } ++ ++ err = av_frame_copy_props(output_frame, input_frame); ++ if (err < 0) ++ goto fail; ++ ++ output_frame->interlaced_frame = 0; ++ ++ if (field == 0) { ++ output_frame->pts *= 2; ++ } else { ++ int64_t cur_pts = ctx->frames[0]->pts; ++ int64_t next_pts = ctx->frames[1]->pts; ++ ++ if (next_pts != AV_NOPTS_VALUE && cur_pts != AV_NOPTS_VALUE) { ++ output_frame->pts = next_pts + cur_pts; ++ } else { ++ output_frame->pts = AV_NOPTS_VALUE; ++ } ++ } ++ av_log(priv, AV_LOG_DEBUG, "pts: %"PRId64" (field %d)\n", output_frame->pts, field); ++ ++ return ff_filter_frame(outlink, output_frame); ++ ++fail: ++ av_frame_free(&output_frame); ++ return err; ++} ++ ++static int deint_v4l2m2m_config_props(AVFilterLink *outlink) ++{ ++ AVFilterLink *inlink = outlink->src->inputs[0]; ++ AVFilterContext *avctx = outlink->src; ++ DeintV4L2M2MContext *priv = avctx->priv; ++ DeintV4L2M2MContextShared *ctx = priv->shared; ++ int ret; ++ ++ ctx->height = avctx->inputs[0]->h; ++ ctx->width = avctx->inputs[0]->w; ++ ++ outlink->frame_rate = av_mul_q(inlink->frame_rate, ++ (AVRational){ 2, 1 }); ++ outlink->time_base = av_mul_q(inlink->time_base, ++ (AVRational){ 1, 2 }); ++ ++ ret = deint_v4l2m2m_find_device(ctx); ++ if (ret) ++ return ret; ++ ++ if (!inlink->hw_frames_ctx) { ++ av_log(priv, AV_LOG_ERROR, "No hw context provided on input\n"); ++ return AVERROR(EINVAL); ++ } ++ ++ ctx->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); ++ if (!ctx->hw_frames_ctx) ++ return AVERROR(ENOMEM); ++ ++ return 0; ++} ++ ++static int deint_v4l2m2m_query_formats(AVFilterContext *avctx) ++{ ++ static const enum AVPixelFormat pixel_formats[] = { ++ AV_PIX_FMT_DRM_PRIME, ++ AV_PIX_FMT_NONE, ++ }; ++ ++ return ff_set_common_formats(avctx, ff_make_format_list(pixel_formats)); ++} ++ ++static int deint_v4l2m2m_filter_frame(AVFilterLink *link, AVFrame *in) ++{ ++ AVFilterContext *avctx = link->dst; ++ DeintV4L2M2MContext *priv = avctx->priv; ++ DeintV4L2M2MContextShared *ctx = priv->shared; ++ V4L2Queue *capture = &ctx->capture; ++ V4L2Queue *output = &ctx->output; ++ int ret; ++ ++ av_log(priv, AV_LOG_DEBUG, "input pts: %"PRId64"\n", in->pts); ++ if (!ctx->frame_count) { ++ AVDRMFrameDescriptor *drm_desc = (AVDRMFrameDescriptor *)in->data[0]; ++ unsigned int field; ++ ++ ctx->orig_width = drm_desc->layers[0].planes[0].pitch; ++ ctx->orig_height = drm_desc->layers[0].planes[1].offset / ctx->orig_width; ++ ++ if (in->top_field_first) ++ field = V4L2_FIELD_INTERLACED_TB; ++ else ++ field = V4L2_FIELD_INTERLACED_BT; ++ ++ ret = deint_v4l2m2m_set_format(output, field, ctx->orig_width, ctx->orig_height); ++ if (ret) ++ return ret; ++ ++ ret = deint_v4l2m2m_set_format(capture, V4L2_FIELD_NONE, ctx->orig_width, ctx->orig_height); ++ if (ret) ++ return ret; ++ ++ ret = deint_v4l2m2m_allocate_buffers(capture); ++ if (ret) ++ return ret; ++ ++ ret = deint_v4l2m2m_streamon(capture); ++ if (ret) ++ return ret; ++ ++ ret = deint_v4l2m2m_allocate_buffers(output); ++ if (ret) ++ return ret; ++ ++ ret = deint_v4l2m2m_streamon(output); ++ if (ret) ++ return ret; ++ } ++ ++ if (ctx->frame_count < 2) { ++ ctx->frames[ctx->frame_count++] = in; ++ } else { ++ av_frame_free(&ctx->frames[0]); ++ ctx->frames[0] = ctx->frames[1]; ++ ctx->frames[1] = in; ++ } ++ ++ ret = deint_v4l2m2m_enqueue(output, in); ++ if (ret) ++ return ret; ++ ++ if (ctx->frame_count == 2) { ++ ret = deint_v4l2m2m_dequeue(avctx, ctx->frames[0], 0); ++ if (ret) ++ return ret; ++ ++ ret = deint_v4l2m2m_dequeue(avctx, ctx->frames[0], 1); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static av_cold int deint_v4l2m2m_init(AVFilterContext *avctx) ++{ ++ DeintV4L2M2MContext *priv = avctx->priv; ++ DeintV4L2M2MContextShared *ctx; ++ ++ ctx = av_mallocz(sizeof(DeintV4L2M2MContextShared)); ++ if (!ctx) ++ return AVERROR(ENOMEM); ++ ++ priv->shared = ctx; ++ ctx->fd = -1; ++ ctx->output.ctx = ctx; ++ ctx->output.num_buffers = 6; ++ ctx->capture.ctx = ctx; ++ ctx->capture.num_buffers = 6; ++ ctx->done = 0; ++ atomic_init(&ctx->refcount, 1); ++ ++ return 0; ++} ++ ++static void deint_v4l2m2m_uninit(AVFilterContext *avctx) ++{ ++ DeintV4L2M2MContext *priv = avctx->priv; ++ DeintV4L2M2MContextShared *ctx = priv->shared; ++ ++ ctx->done = 1; ++ deint_v4l2m2m_destroy_context(ctx); ++} ++ ++static const AVOption deinterlace_v4l2m2m_options[] = { ++ { NULL }, ++}; ++ ++AVFILTER_DEFINE_CLASS(deinterlace_v4l2m2m); ++ ++static const AVFilterPad deint_v4l2m2m_inputs[] = { ++ { ++ .name = "default", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .filter_frame = deint_v4l2m2m_filter_frame, ++ }, ++ { NULL } ++}; ++ ++static const AVFilterPad deint_v4l2m2m_outputs[] = { ++ { ++ .name = "default", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .config_props = deint_v4l2m2m_config_props, ++ }, ++ { NULL } ++}; ++ ++AVFilter ff_vf_deinterlace_v4l2m2m = { ++ .name = "deinterlace_v4l2m2m", ++ .description = NULL_IF_CONFIG_SMALL("V4L2 M2M deinterlacer"), ++ .priv_size = sizeof(DeintV4L2M2MContext), ++ .init = &deint_v4l2m2m_init, ++ .uninit = &deint_v4l2m2m_uninit, ++ .query_formats = &deint_v4l2m2m_query_formats, ++ .inputs = deint_v4l2m2m_inputs, ++ .outputs = deint_v4l2m2m_outputs, ++ .priv_class = &deinterlace_v4l2m2m_class, ++}; +-- +2.29.2 + diff --git a/projects/Rockchip/patches/ffmpeg/ffmpeg-0003-libavfilter-v4l2deinterlace-dequeue-both-destination.patch b/projects/Rockchip/patches/ffmpeg/ffmpeg-0003-libavfilter-v4l2deinterlace-dequeue-both-destination.patch new file mode 100644 index 0000000000..cefbd9c64d --- /dev/null +++ b/projects/Rockchip/patches/ffmpeg/ffmpeg-0003-libavfilter-v4l2deinterlace-dequeue-both-destination.patch @@ -0,0 +1,230 @@ +From 6bea46839ba23bffaa093bb9ed805d571aaa66ea Mon Sep 17 00:00:00 2001 +From: Alex Bee +Date: Wed, 30 Sep 2020 21:11:34 +0200 +Subject: [PATCH] libavfilter: v4l2deinterlace: dequeue both destination + buffers on time + +Signed-off-by: Alex Bee +--- + libavfilter/vf_deinterlace_v4l2m2m.c | 140 +++++++++++++++++---------- + 1 file changed, 88 insertions(+), 52 deletions(-) + +diff --git a/libavfilter/vf_deinterlace_v4l2m2m.c b/libavfilter/vf_deinterlace_v4l2m2m.c +index 1029e5b620fd..72d28333ffa7 100644 +--- a/libavfilter/vf_deinterlace_v4l2m2m.c ++++ b/libavfilter/vf_deinterlace_v4l2m2m.c +@@ -89,8 +89,14 @@ typedef struct DeintV4L2M2MContextShared { + + AVBufferRef *hw_frames_ctx; + +- int frame_count; +- AVFrame *frames[2]; ++ /* ++ * TODO: check if its really neccessary to hold this ++ * ref, it's only used for freeing av_frame on decoding ++ * end/abort ++ */ ++ AVFrame *cur_in_frame; ++ AVFrame *prev_in_frame; ++ unsigned int field_order; + + V4L2Queue output; + V4L2Queue capture; +@@ -557,8 +563,11 @@ static void deint_v4l2m2m_destroy_context(DeintV4L2M2MContextShared *ctx) + close(capture->buffers[i].fd); + } + +- for (i = 0; i < ctx->frame_count; i++) +- av_frame_free(&ctx->frames[i]); ++ if (ctx->cur_in_frame) ++ av_frame_free(&ctx->cur_in_frame); ++ ++ if (ctx->prev_in_frame) ++ av_frame_free(&ctx->prev_in_frame); + + av_buffer_unref(&ctx->hw_frames_ctx); + +@@ -652,49 +661,79 @@ static int deint_v4l2m2m_dequeue_frame(V4L2Queue *queue, AVFrame* frame, int tim + return 0; + } + +-static int deint_v4l2m2m_dequeue(AVFilterContext *avctx, AVFrame *input_frame, int field) ++static int deint_v4l2m2m_dequeue(AVFilterContext *avctx, AVFrame *input_frame) + { + DeintV4L2M2MContext *priv = avctx->priv; + DeintV4L2M2MContextShared *ctx = priv->shared; + AVFilterLink *outlink = avctx->outputs[0]; +- AVFrame *output_frame; ++ AVFrame *output_frame_1, *output_frame_2; ++ int64_t first_pts = AV_NOPTS_VALUE; + int err; + +- output_frame = av_frame_alloc(); ++ av_log(priv, AV_LOG_DEBUG, "input pts: %"PRId64" (field %d)\n", ++ input_frame->pts, ctx->field_order); + +- if (!output_frame) ++ output_frame_1 = av_frame_alloc(); ++ if (!output_frame_1) + return AVERROR(ENOMEM); + +- err = deint_v4l2m2m_dequeue_frame(&ctx->capture, output_frame, 500); ++ err = deint_v4l2m2m_dequeue_frame(&ctx->capture, output_frame_1, 500); + if (err < 0) { +- av_log(priv, AV_LOG_ERROR, "no frame (field %d)\n", field); +- goto fail; ++ av_log(priv, AV_LOG_ERROR, "no 1st frame (field %d)\n", ctx->field_order); ++ goto fail_out1; + } + +- err = av_frame_copy_props(output_frame, input_frame); ++ err = av_frame_copy_props(output_frame_1, input_frame); + if (err < 0) +- goto fail; ++ goto fail_out1; + +- output_frame->interlaced_frame = 0; ++ output_frame_1->interlaced_frame = 0; + +- if (field == 0) { +- output_frame->pts *= 2; +- } else { +- int64_t cur_pts = ctx->frames[0]->pts; +- int64_t next_pts = ctx->frames[1]->pts; ++ output_frame_2 = av_frame_alloc(); ++ if (!output_frame_2) { ++ err = AVERROR(ENOMEM); ++ goto fail_out1; ++ } ++ ++ err = deint_v4l2m2m_dequeue_frame(&ctx->capture, output_frame_2, 500); ++ if (err < 0) { ++ av_log(priv, AV_LOG_ERROR, "no 2nd frame (field %d)\n", ctx->field_order); ++ goto fail_out2; ++ } ++ ++ err = av_frame_copy_props(output_frame_2, input_frame); ++ if (err < 0) ++ goto fail_out2; ++ ++ output_frame_2->interlaced_frame = 0; + +- if (next_pts != AV_NOPTS_VALUE && cur_pts != AV_NOPTS_VALUE) { +- output_frame->pts = next_pts + cur_pts; +- } else { +- output_frame->pts = AV_NOPTS_VALUE; +- } ++ if (ctx->prev_in_frame && ctx->prev_in_frame->pts != AV_NOPTS_VALUE ++ && input_frame->pts != AV_NOPTS_VALUE) { ++ first_pts = (ctx->prev_in_frame->pts + input_frame->pts) / 2; ++ av_log(priv, AV_LOG_DEBUG, "calculated first pts %"PRId64"\n", first_pts); + } +- av_log(priv, AV_LOG_DEBUG, "pts: %"PRId64" (field %d)\n", output_frame->pts, field); + +- return ff_filter_frame(outlink, output_frame); ++ output_frame_1->pts = first_pts; ++ ++ err = ff_filter_frame(outlink, output_frame_1); ++ if (err < 0) { ++ av_frame_free(&output_frame_2); ++ return err; ++ } ++ err = ff_filter_frame(outlink, output_frame_2); ++ ++ if (err < 0) ++ return err; ++ ++ av_log(priv, AV_LOG_DEBUG, "1st frame pts: %"PRId64" 2nd frame pts: %"PRId64" first pts: %"PRId64" (field %d)\n", ++ output_frame_1->pts, output_frame_2->pts, first_pts, ctx->field_order); ++ ++ return 0; + +-fail: +- av_frame_free(&output_frame); ++fail_out2: ++ av_frame_free(&output_frame_2); ++fail_out1: ++ av_frame_free(&output_frame_1); + return err; + } + +@@ -749,20 +788,22 @@ static int deint_v4l2m2m_filter_frame(AVFilterLink *link, AVFrame *in) + V4L2Queue *output = &ctx->output; + int ret; + +- av_log(priv, AV_LOG_DEBUG, "input pts: %"PRId64"\n", in->pts); +- if (!ctx->frame_count) { ++ av_log(priv, AV_LOG_DEBUG, "input pts: %"PRId64" field :%d interlaced: %d\n", ++ in->pts, in->top_field_first, in->interlaced_frame); ++ ++ ctx->cur_in_frame = in; ++ ++ if (ctx->field_order == V4L2_FIELD_ANY) { + AVDRMFrameDescriptor *drm_desc = (AVDRMFrameDescriptor *)in->data[0]; +- unsigned int field; +- + ctx->orig_width = drm_desc->layers[0].planes[0].pitch; + ctx->orig_height = drm_desc->layers[0].planes[1].offset / ctx->orig_width; + +- if (in->top_field_first) +- field = V4L2_FIELD_INTERLACED_TB; ++ if (in->top_field_first) ++ ctx->field_order = V4L2_FIELD_INTERLACED_TB; + else +- field = V4L2_FIELD_INTERLACED_BT; ++ ctx->field_order = V4L2_FIELD_INTERLACED_BT; + +- ret = deint_v4l2m2m_set_format(output, field, ctx->orig_width, ctx->orig_height); ++ ret = deint_v4l2m2m_set_format(output, ctx->field_order, ctx->orig_width, ctx->orig_height); + if (ret) + return ret; + +@@ -787,27 +828,19 @@ static int deint_v4l2m2m_filter_frame(AVFilterLink *link, AVFrame *in) + return ret; + } + +- if (ctx->frame_count < 2) { +- ctx->frames[ctx->frame_count++] = in; +- } else { +- av_frame_free(&ctx->frames[0]); +- ctx->frames[0] = ctx->frames[1]; +- ctx->frames[1] = in; +- } +- + ret = deint_v4l2m2m_enqueue(output, in); + if (ret) + return ret; + +- if (ctx->frame_count == 2) { +- ret = deint_v4l2m2m_dequeue(avctx, ctx->frames[0], 0); +- if (ret) +- return ret; ++ ret = deint_v4l2m2m_dequeue(avctx, in); ++ if (ret) ++ return ret; + +- ret = deint_v4l2m2m_dequeue(avctx, ctx->frames[0], 1); +- if (ret) +- return ret; +- } ++ if (ctx->prev_in_frame) ++ av_frame_free(&ctx->prev_in_frame); ++ ++ ctx->prev_in_frame = in; ++ ctx->cur_in_frame = NULL; + + return 0; + } +@@ -828,6 +861,9 @@ static av_cold int deint_v4l2m2m_init(AVFilterContext *avctx) + ctx->capture.ctx = ctx; + ctx->capture.num_buffers = 6; + ctx->done = 0; ++ ctx->field_order = V4L2_FIELD_ANY; ++ ctx->cur_in_frame = NULL; ++ ctx->prev_in_frame = NULL; + atomic_init(&ctx->refcount, 1); + + return 0; +-- +2.29.2 + diff --git a/projects/Rockchip/patches/kodi/0001-WIP-DVDVideoCodecDRMPRIME-add-support-for-filters.patch b/projects/Rockchip/patches/kodi/0001-WIP-DVDVideoCodecDRMPRIME-add-support-for-filters.patch new file mode 100644 index 0000000000..9e11fbd55c --- /dev/null +++ b/projects/Rockchip/patches/kodi/0001-WIP-DVDVideoCodecDRMPRIME-add-support-for-filters.patch @@ -0,0 +1,145 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jonas Karlman +Date: Sun, 20 Oct 2019 17:10:07 +0000 +Subject: [PATCH] WIP: DVDVideoCodecDRMPRIME: add support for filters + +--- + .../DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp | 68 ++++++++++++++++--- + .../DVDCodecs/Video/DVDVideoCodecDRMPRIME.h | 10 +++ + 2 files changed, 69 insertions(+), 9 deletions(-) + +diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp +index 8024c20816..7507c12e9a 100644 +--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp ++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp +@@ -27,6 +27,8 @@ + extern "C" + { + #include ++#include ++#include + #include + #include + #include +@@ -525,18 +527,30 @@ void CDVDVideoCodecDRMPRIME::SetPictureParams(VideoPicture* pVideoPicture) + pVideoPicture->dts = DVD_NOPTS_VALUE; + } + +-CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::GetPicture(VideoPicture* pVideoPicture) ++CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::ProcessFilterIn() + { +- if (m_codecControlFlags & DVD_CODEC_CTRL_DRAIN) +- Drain(); ++ if (!m_pFilterIn) ++ return VC_PICTURE; + +- if (pVideoPicture->videoBuffer) ++ int ret = av_buffersrc_add_frame(m_pFilterIn, m_pFrame); ++ if (ret < 0) + { +- pVideoPicture->videoBuffer->Release(); +- pVideoPicture->videoBuffer = nullptr; ++ char err[AV_ERROR_MAX_STRING_SIZE] = {}; ++ av_strerror(ret, err, AV_ERROR_MAX_STRING_SIZE); ++ CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::{} - buffersrc add frame failed: {} ({})", ++ __FUNCTION__, err, ret); ++ return VC_ERROR; + } + +- int ret = avcodec_receive_frame(m_pCodecContext, m_pFrame); ++ return ProcessFilterOut(); ++} ++ ++CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::ProcessFilterOut() ++{ ++ if (!m_pFilterOut) ++ return VC_EOF; ++ ++ int ret = av_buffersink_get_frame(m_pFilterOut, m_pFrame); + if (ret == AVERROR(EAGAIN)) + return VC_BUFFER; + else if (ret == AVERROR_EOF) +@@ -553,11 +567,47 @@ CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::GetPicture(VideoPicture* pVideo + { + char err[AV_ERROR_MAX_STRING_SIZE] = {}; + av_strerror(ret, err, AV_ERROR_MAX_STRING_SIZE); +- CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::{} - receive frame failed: {} ({})", __FUNCTION__, +- err, ret); ++ CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::{} - buffersink get frame failed: {} ({})", ++ __FUNCTION__, err, ret); + return VC_ERROR; + } + ++ return VC_PICTURE; ++} ++ ++CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::GetPicture(VideoPicture* pVideoPicture) ++{ ++ if (m_codecControlFlags & DVD_CODEC_CTRL_DRAIN) ++ Drain(); ++ ++ if (pVideoPicture->videoBuffer) ++ { ++ pVideoPicture->videoBuffer->Release(); ++ pVideoPicture->videoBuffer = nullptr; ++ } ++ ++ auto result = ProcessFilterOut(); ++ if (result != VC_PICTURE) ++ { ++ int ret = avcodec_receive_frame(m_pCodecContext, m_pFrame); ++ if (ret == AVERROR(EAGAIN)) ++ return VC_BUFFER; ++ else if (ret == AVERROR_EOF) ++ return VC_EOF; ++ else if (ret) ++ { ++ char err[AV_ERROR_MAX_STRING_SIZE] = {}; ++ av_strerror(ret, err, AV_ERROR_MAX_STRING_SIZE); ++ CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::{} - receive frame failed: {} ({})", ++ __FUNCTION__, err, ret); ++ return VC_ERROR; ++ } ++ ++ result = ProcessFilterIn(); ++ if (result != VC_PICTURE) ++ return result; ++ } ++ + SetPictureParams(pVideoPicture); + + if (IsSupportedHwFormat(static_cast(m_pFrame->format))) +diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h +index 77d066c3d9..7112d1b48a 100644 +--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h ++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h +@@ -14,6 +14,11 @@ + + #include + ++extern "C" ++{ ++#include ++} ++ + class CDVDVideoCodecDRMPRIME : public CDVDVideoCodec + { + public: +@@ -35,6 +40,8 @@ protected: + void Drain(); + void SetPictureParams(VideoPicture* pVideoPicture); + void UpdateProcessInfo(struct AVCodecContext* avctx, const enum AVPixelFormat fmt); ++ CDVDVideoCodec::VCReturn ProcessFilterIn(); ++ CDVDVideoCodec::VCReturn ProcessFilterOut(); + static enum AVPixelFormat GetFormat(struct AVCodecContext* avctx, const enum AVPixelFormat* fmt); + static int GetBuffer(struct AVCodecContext* avctx, AVFrame* frame, int flags); + +@@ -43,5 +50,8 @@ protected: + CDVDStreamInfo m_hints; + AVCodecContext* m_pCodecContext = nullptr; + AVFrame* m_pFrame = nullptr; ++ AVFilterGraph* m_pFilterGraph = nullptr; ++ AVFilterContext* m_pFilterIn = nullptr; ++ AVFilterContext* m_pFilterOut = nullptr; + std::shared_ptr m_videoBufferPool; + }; diff --git a/projects/Rockchip/patches/kodi/0002-WIP-DRMPRIME-deinterlace-filter.patch b/projects/Rockchip/patches/kodi/0002-WIP-DRMPRIME-deinterlace-filter.patch new file mode 100644 index 0000000000..0e19bb6788 --- /dev/null +++ b/projects/Rockchip/patches/kodi/0002-WIP-DRMPRIME-deinterlace-filter.patch @@ -0,0 +1,500 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Thu, 26 Dec 2019 11:01:51 +0100 +Subject: [PATCH] WIP: DRMPRIME deinterlace filter + +--- + .../DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp | 368 +++++++++++++++--- + .../DVDCodecs/Video/DVDVideoCodecDRMPRIME.h | 9 +- + 2 files changed, 322 insertions(+), 55 deletions(-) + +diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp +index 7507c12e9a..4759dde3f9 100644 +--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp ++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp +@@ -79,12 +79,15 @@ CDVDVideoCodecDRMPRIME::CDVDVideoCodecDRMPRIME(CProcessInfo& processInfo) + : CDVDVideoCodec(processInfo) + { + m_pFrame = av_frame_alloc(); ++ m_pFilterFrame = av_frame_alloc(); + m_videoBufferPool = std::make_shared(); + } + + CDVDVideoCodecDRMPRIME::~CDVDVideoCodecDRMPRIME() + { + av_frame_free(&m_pFrame); ++ av_frame_free(&m_pFilterFrame); ++ FilterClose(); + avcodec_free_context(&m_pCodecContext); + } + +@@ -341,8 +344,19 @@ bool CDVDVideoCodecDRMPRIME::Open(CDVDStreamInfo& hints, CDVDCodecOptions& optio + } + + UpdateProcessInfo(m_pCodecContext, m_pCodecContext->pix_fmt); +- m_processInfo.SetVideoDeintMethod("none"); ++ m_processInfo.SetVideoInterlaced(false); + m_processInfo.SetVideoDAR(hints.aspect); ++ m_processInfo.SetVideoDeintMethod("none"); ++ ++ FilterTest(); ++ ++ if (!m_deintFilterName.empty()) ++ { ++ std::list methods; ++ methods.push_back(EINTERLACEMETHOD::VS_INTERLACEMETHOD_DEINTERLACE); ++ m_processInfo.UpdateDeinterlacingMethods(methods); ++ m_processInfo.SetDeinterlacingMethodDefault(EINTERLACEMETHOD::VS_INTERLACEMETHOD_DEINTERLACE); ++ } + + return true; + } +@@ -405,6 +419,8 @@ void CDVDVideoCodecDRMPRIME::Reset() + return; + + Drain(); ++ m_filters.clear(); ++ FilterClose(); + + do + { +@@ -443,7 +459,7 @@ void CDVDVideoCodecDRMPRIME::Drain() + } + } + +-void CDVDVideoCodecDRMPRIME::SetPictureParams(VideoPicture* pVideoPicture) ++bool CDVDVideoCodecDRMPRIME::SetPictureParams(VideoPicture* pVideoPicture) + { + pVideoPicture->iWidth = m_pFrame->width; + pVideoPicture->iHeight = m_pFrame->height; +@@ -525,13 +541,232 @@ void CDVDVideoCodecDRMPRIME::SetPictureParams(VideoPicture* pVideoPicture) + ? DVD_NOPTS_VALUE + : static_cast(pts) * DVD_TIME_BASE / AV_TIME_BASE; + pVideoPicture->dts = DVD_NOPTS_VALUE; ++ ++ if (IsSupportedHwFormat(static_cast(m_pFrame->format))) ++ { ++ CVideoBufferDRMPRIMEFFmpeg* buffer = ++ dynamic_cast(m_videoBufferPool->Get()); ++ buffer->SetPictureParams(*pVideoPicture); ++ buffer->SetRef(m_pFrame); ++ pVideoPicture->videoBuffer = buffer; ++ } ++ else if (m_pFrame->opaque) ++ { ++ CVideoBufferDMA* buffer = static_cast(m_pFrame->opaque); ++ buffer->SetPictureParams(*pVideoPicture); ++ buffer->Acquire(); ++ buffer->SyncEnd(); ++ buffer->SetDimensions(m_pFrame->width, m_pFrame->height); ++ ++ pVideoPicture->videoBuffer = buffer; ++ av_frame_unref(m_pFrame); ++ } ++ ++ if (!pVideoPicture->videoBuffer) ++ { ++ CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::{} - videoBuffer:nullptr format:{}", __FUNCTION__, ++ av_get_pix_fmt_name(static_cast(m_pFrame->format))); ++ av_frame_unref(m_pFrame); ++ return false; ++ } ++ ++ return true; + } + +-CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::ProcessFilterIn() ++void CDVDVideoCodecDRMPRIME::FilterTest() ++{ ++ const AVFilter* filter; ++ void* opaque{}; ++ ++ m_deintFilterName.clear(); ++ ++ while ((filter = av_filter_iterate(&opaque)) != nullptr) ++ { ++ std::string name(filter->name); ++ ++ if (name.find("deinterlace") != std::string::npos) ++ { ++ if (FilterOpen(name, true)) ++ { ++ m_deintFilterName = name; ++ ++ CLog::Log(LOGDEBUG, "CDVDVideoCodecDRMPRIME::{} - found deinterlacing filter {}", ++ __FUNCTION__, name); ++ ++ return; ++ } ++ } ++ } ++ ++ CLog::Log(LOGDEBUG, "CDVDVideoCodecDRMPRIME::{} - no deinterlacing filter found", ++ __FUNCTION__); ++} ++ ++bool CDVDVideoCodecDRMPRIME::FilterOpen(const std::string& filters, bool test) ++{ ++ int result; ++ ++ if (m_pFilterGraph) ++ FilterClose(); ++ ++ if (filters.empty()) ++ return true; ++ ++ if (!(m_pFilterGraph = avfilter_graph_alloc())) ++ { ++ CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::FilterOpen - unable to alloc filter graph"); ++ return false; ++ } ++ ++ const AVFilter* srcFilter = avfilter_get_by_name("buffer"); ++ const AVFilter* outFilter = avfilter_get_by_name("buffersink"); ++ enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_DRM_PRIME, AV_PIX_FMT_NONE }; ++ ++ std::string args = StringUtils::Format("video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" ++ "pixel_aspect=%d/%d:sws_param=flags=2", ++ m_pCodecContext->width, ++ m_pCodecContext->height, ++ m_pCodecContext->pix_fmt, ++ m_pCodecContext->time_base.num ? ++ m_pCodecContext->time_base.num : 1, ++ m_pCodecContext->time_base.num ? ++ m_pCodecContext->time_base.den : 1, ++ m_pCodecContext->sample_aspect_ratio.num != 0 ? ++ m_pCodecContext->sample_aspect_ratio.num : 1, ++ m_pCodecContext->sample_aspect_ratio.num != 0 ? ++ m_pCodecContext->sample_aspect_ratio.den : 1); ++ ++ result = avfilter_graph_create_filter(&m_pFilterIn, srcFilter, "src", ++ args.c_str(), NULL, m_pFilterGraph); ++ if (result < 0) ++ { ++ char err[AV_ERROR_MAX_STRING_SIZE] = {}; ++ av_strerror(result, err, AV_ERROR_MAX_STRING_SIZE); ++ CLog::Log(LOGERROR, ++ "CDVDVideoCodecDRMPRIME::FilterOpen - avfilter_graph_create_filter: src: {} ({})", ++ err, result); ++ return false; ++ } ++ ++ AVBufferSrcParameters *par = av_buffersrc_parameters_alloc(); ++ if (!par) ++ { ++ CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::FilterOpen - unable to alloc buffersrc"); ++ return false; ++ } ++ ++ memset(par, 0, sizeof(*par)); ++ par->format = AV_PIX_FMT_NONE; ++ par->hw_frames_ctx = m_pCodecContext->hw_device_ctx; ++ ++ result = av_buffersrc_parameters_set(m_pFilterIn, par); ++ if (result < 0) ++ { ++ char err[AV_ERROR_MAX_STRING_SIZE] = {}; ++ av_strerror(result, err, AV_ERROR_MAX_STRING_SIZE); ++ CLog::Log(LOGERROR, ++ "CDVDVideoCodecDRMPRIME::FilterOpen - av_buffersrc_parameters_set: {} ({})", ++ err, result); ++ return false; ++ } ++ av_freep(&par); ++ ++ result = avfilter_graph_create_filter(&m_pFilterOut, outFilter, "out", ++ NULL, NULL, m_pFilterGraph); ++ if (result < 0) ++ { ++ char err[AV_ERROR_MAX_STRING_SIZE] = {}; ++ av_strerror(result, err, AV_ERROR_MAX_STRING_SIZE); ++ CLog::Log(LOGERROR, ++ "CDVDVideoCodecDRMPRIME::FilterOpen - avfilter_graph_create_filter: out: {} ({})", ++ err, result); ++ return false; ++ } ++ ++ result = av_opt_set_int_list(m_pFilterOut, "pix_fmts", &pix_fmts[0], ++ AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); ++ if (result < 0) ++ { ++ CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::FilterOpen - failed settings pix formats"); ++ return false; ++ } ++ ++ AVFilterInOut* outputs = avfilter_inout_alloc(); ++ AVFilterInOut* inputs = avfilter_inout_alloc(); ++ ++ outputs->name = av_strdup("in"); ++ outputs->filter_ctx = m_pFilterIn; ++ outputs->pad_idx = 0; ++ outputs->next = nullptr; ++ ++ inputs->name = av_strdup("out"); ++ inputs->filter_ctx = m_pFilterOut; ++ inputs->pad_idx = 0; ++ inputs->next = nullptr; ++ ++ result = avfilter_graph_parse_ptr(m_pFilterGraph, filters.c_str(), &inputs, &outputs, NULL); ++ avfilter_inout_free(&outputs); ++ avfilter_inout_free(&inputs); ++ ++ if (result < 0) ++ { ++ CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::FilterOpen - avfilter_graph_parse"); ++ return false; ++ } ++ ++ if ((result = avfilter_graph_config(m_pFilterGraph, nullptr)) < 0) ++ { ++ char err[AV_ERROR_MAX_STRING_SIZE] = {}; ++ av_strerror(result, err, AV_ERROR_MAX_STRING_SIZE); ++ CLog::Log(LOGDEBUG, "CDVDVideoCodecDRMPRIME::FilterOpen - avfilter_graph_config: {} ({})", ++ err, result); ++ return false; ++ } ++ ++ if (test) ++ { ++ FilterClose(); ++ return true; ++ } ++ ++ if (filters.find("deinterlace") != std::string::npos) ++ { ++ m_processInfo.SetVideoDeintMethod(filters); ++ } ++ else ++ { ++ m_processInfo.SetVideoDeintMethod("none"); ++ } ++ ++ if (CServiceBroker::GetLogging().CanLogComponent(LOGVIDEO)) ++ { ++ char* graphDump = avfilter_graph_dump(m_pFilterGraph, nullptr); ++ if (graphDump) ++ { ++ CLog::Log(LOGDEBUG, "CDVDVideoCodecDRMPRIME::FilterOpen - Final filter graph:\n%s", ++ graphDump); ++ av_freep(&graphDump); ++ } ++ } ++ ++ return true; ++} ++ ++void CDVDVideoCodecDRMPRIME::FilterClose() + { +- if (!m_pFilterIn) +- return VC_PICTURE; ++ if (m_pFilterGraph) ++ { ++ CLog::Log(LOGDEBUG, LOGVIDEO, "CDVDVideoCodecDRMPRIME::FilterClose - Freeing filter graph"); ++ avfilter_graph_free(&m_pFilterGraph); ++ ++ // Disposed by above code ++ m_pFilterIn = nullptr; ++ m_pFilterOut = nullptr; ++ } ++} + ++CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::ProcessFilterIn() ++{ + int ret = av_buffersrc_add_frame(m_pFilterIn, m_pFrame); + if (ret < 0) + { +@@ -547,21 +782,14 @@ CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::ProcessFilterIn() + + CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::ProcessFilterOut() + { +- if (!m_pFilterOut) +- return VC_EOF; +- +- int ret = av_buffersink_get_frame(m_pFilterOut, m_pFrame); ++ int ret = av_buffersink_get_frame(m_pFilterOut, m_pFilterFrame); + if (ret == AVERROR(EAGAIN)) + return VC_BUFFER; + else if (ret == AVERROR_EOF) + { +- if (m_codecControlFlags & DVD_CODEC_CTRL_DRAIN) +- { +- CLog::Log(LOGDEBUG, "CDVDVideoCodecDRMPRIME::{} - flush buffers", __FUNCTION__); +- avcodec_flush_buffers(m_pCodecContext); +- SetCodecControl(m_codecControlFlags & ~DVD_CODEC_CTRL_DRAIN); +- } +- return VC_EOF; ++ ret = av_buffersink_get_frame(m_pFilterOut, m_pFilterFrame); ++ if (ret < 0) ++ return VC_BUFFER; + } + else if (ret) + { +@@ -572,9 +800,27 @@ CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::ProcessFilterOut() + return VC_ERROR; + } + ++ av_frame_unref(m_pFrame); ++ av_frame_move_ref(m_pFrame, m_pFilterFrame); ++ + return VC_PICTURE; + } + ++std::string CDVDVideoCodecDRMPRIME::GetFilterChain(bool interlaced) ++{ ++ // ask codec to do deinterlacing if possible ++ EINTERLACEMETHOD mInt = m_processInfo.GetVideoSettings().m_InterlaceMethod; ++ std::string filterChain; ++ ++ if (!m_processInfo.Supports(mInt)) ++ mInt = m_processInfo.GetFallbackDeintMethod(); ++ ++ if (mInt != VS_INTERLACEMETHOD_NONE && interlaced && !m_deintFilterName.empty()) ++ filterChain += m_deintFilterName; ++ ++ return filterChain; ++} ++ + CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::GetPicture(VideoPicture* pVideoPicture) + { + if (m_codecControlFlags & DVD_CODEC_CTRL_DRAIN) +@@ -586,57 +832,71 @@ CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::GetPicture(VideoPicture* pVideo + pVideoPicture->videoBuffer = nullptr; + } + +- auto result = ProcessFilterOut(); +- if (result != VC_PICTURE) ++ if (m_pFilterGraph) + { +- int ret = avcodec_receive_frame(m_pCodecContext, m_pFrame); +- if (ret == AVERROR(EAGAIN)) +- return VC_BUFFER; +- else if (ret == AVERROR_EOF) +- return VC_EOF; +- else if (ret) ++ auto ret = ProcessFilterOut(); ++ if (ret == VC_PICTURE) + { +- char err[AV_ERROR_MAX_STRING_SIZE] = {}; +- av_strerror(ret, err, AV_ERROR_MAX_STRING_SIZE); +- CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::{} - receive frame failed: {} ({})", +- __FUNCTION__, err, ret); +- return VC_ERROR; ++ if (!SetPictureParams(pVideoPicture)) ++ return VC_ERROR; ++ return VC_PICTURE; + } ++ else if (ret != VC_BUFFER) ++ { ++ return ret; ++ } ++ } + +- result = ProcessFilterIn(); +- if (result != VC_PICTURE) +- return result; ++ int ret = avcodec_receive_frame(m_pCodecContext, m_pFrame); ++ if (ret == AVERROR(EAGAIN)) ++ return VC_BUFFER; ++ else if (ret == AVERROR_EOF) ++ return VC_EOF; ++ else if (ret) ++ { ++ char err[AV_ERROR_MAX_STRING_SIZE] = {}; ++ av_strerror(ret, err, AV_ERROR_MAX_STRING_SIZE); ++ CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::{} - receive frame failed: {} ({})", ++ __FUNCTION__, err, ret); ++ return VC_ERROR; + } + +- SetPictureParams(pVideoPicture); ++ if (!m_processInfo.GetVideoInterlaced() && m_pFrame->interlaced_frame) ++ m_processInfo.SetVideoInterlaced(true); + +- if (IsSupportedHwFormat(static_cast(m_pFrame->format))) ++ std::string filterChain = GetFilterChain(m_pFrame->interlaced_frame); ++ if (!filterChain.empty()) + { +- CVideoBufferDRMPRIMEFFmpeg* buffer = +- dynamic_cast(m_videoBufferPool->Get()); +- buffer->SetPictureParams(*pVideoPicture); +- buffer->SetRef(m_pFrame); +- pVideoPicture->videoBuffer = buffer; ++ bool reopenFilter = false; ++ if (m_filters != filterChain) ++ reopenFilter = true; ++ ++ if (m_pFilterGraph && ++ (m_pFilterIn->outputs[0]->w != m_pCodecContext->width || ++ m_pFilterIn->outputs[0]->h != m_pCodecContext->height)) ++ reopenFilter = true; ++ ++ if (reopenFilter) ++ { ++ m_filters = filterChain; ++ if (!FilterOpen(filterChain, false)) ++ FilterClose(); ++ } ++ ++ if (m_pFilterGraph) ++ { ++ if (ProcessFilterIn() != VC_PICTURE) ++ return VC_NONE; ++ } + } +- else if (m_pFrame->opaque) ++ else + { +- CVideoBufferDMA* buffer = static_cast(m_pFrame->opaque); +- buffer->SetPictureParams(*pVideoPicture); +- buffer->Acquire(); +- buffer->SyncEnd(); +- buffer->SetDimensions(m_pFrame->width, m_pFrame->height); +- +- pVideoPicture->videoBuffer = buffer; +- av_frame_unref(m_pFrame); ++ m_filters.clear(); ++ FilterClose(); + } + +- if (!pVideoPicture->videoBuffer) +- { +- CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::{} - videoBuffer:nullptr format:{}", __FUNCTION__, +- av_get_pix_fmt_name(static_cast(m_pFrame->format))); +- av_frame_unref(m_pFrame); ++ if (!SetPictureParams(pVideoPicture)) + return VC_ERROR; +- } + + return VC_PICTURE; + } +diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h +index 7112d1b48a..13bec95135 100644 +--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h ++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h +@@ -38,18 +38,25 @@ public: + + protected: + void Drain(); +- void SetPictureParams(VideoPicture* pVideoPicture); ++ bool SetPictureParams(VideoPicture* pVideoPicture); + void UpdateProcessInfo(struct AVCodecContext* avctx, const enum AVPixelFormat fmt); + CDVDVideoCodec::VCReturn ProcessFilterIn(); + CDVDVideoCodec::VCReturn ProcessFilterOut(); + static enum AVPixelFormat GetFormat(struct AVCodecContext* avctx, const enum AVPixelFormat* fmt); + static int GetBuffer(struct AVCodecContext* avctx, AVFrame* frame, int flags); ++ bool FilterOpen(const std::string& filters, bool test); ++ void FilterClose(); ++ void FilterTest(); ++ std::string GetFilterChain(bool interlaced); + + std::string m_name; ++ std::string m_deintFilterName; ++ std::string m_filters; + int m_codecControlFlags = 0; + CDVDStreamInfo m_hints; + AVCodecContext* m_pCodecContext = nullptr; + AVFrame* m_pFrame = nullptr; ++ AVFrame* m_pFilterFrame = nullptr; + AVFilterGraph* m_pFilterGraph = nullptr; + AVFilterContext* m_pFilterIn = nullptr; + AVFilterContext* m_pFilterOut = nullptr;