linuxOS_AP05/buildroot/package/ffmpeg/0006-v4l2-support-queue-drm-buffers.patch
2025-06-02 13:59:07 +08:00

777 lines
29 KiB
Diff

From 2f239cf8988ceb37ab7d2d83b53636f9d13db8e1 Mon Sep 17 00:00:00 2001
From: Hertz Wang <wangh@rock-chips.com>
Date: Fri, 2 Nov 2018 19:15:55 +0800
Subject: [PATCH 06/11] v4l2: support queue drm buffers
Test Passed:
ffmpeg -loglevel debug -video_size 1280*720 -pixel_format drm_prime:nv12
-i /dev/video0 -vcodec h264_rkmpp -b 6000000 /userdata/out.mp4
TODO: drm_prime:mjpeg/h264
Change-Id: I92e1db3576162b81a041f3a61e54c213a35ea1f9
Signed-off-by: Hertz Wang <wangh@rock-chips.com>
---
libavcodec/avcodec.h | 15 ++++
libavcodec/avpacket.c | 16 ++++
libavcodec/rawdec.c | 137 ++++++++++++++++------------
libavcodec/utils.c | 30 +++++--
libavdevice/v4l2.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++----
5 files changed, 362 insertions(+), 80 deletions(-)
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index bee2234..ffafe79 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -1473,6 +1473,15 @@ typedef struct AVPacket {
attribute_deprecated
int64_t convergence_duration;
#endif
+ /*
+ * 1. zero copying path for input device, such as camera grab.
+ * AVPacket may be actually an AVFrame.
+ * 2. the API of hwaccel always handle AVFrame while contains the hw buffer
+ * descriptor
+ *
+ * This is a reference to a hwaccel avframe.
+ */
+ AVFrame *hw_frame;
} AVPacket;
#define AV_PKT_FLAG_KEY 0x0001 ///< The packet contains a keyframe
#define AV_PKT_FLAG_CORRUPT 0x0002 ///< The packet content is corrupted
@@ -3924,6 +3933,12 @@ typedef struct AVCodecParameters {
int format;
/**
+ * video only: the pixel format, the value corresponds to enum AVPixelFormat,
+ * except the hw_accel pixel format.
+ */
+ int sw_format;
+
+ /**
* The average bitrate of the encoded data (in bits per second).
*/
int64_t bit_rate;
diff --git a/libavcodec/avpacket.c b/libavcodec/avpacket.c
index e160ad3..20fa203 100644
--- a/libavcodec/avpacket.c
+++ b/libavcodec/avpacket.c
@@ -46,6 +46,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
pkt->buf = NULL;
pkt->side_data = NULL;
pkt->side_data_elems = 0;
+ pkt->hw_frame = NULL;
}
AVPacket *av_packet_alloc(void)
@@ -599,6 +600,7 @@ void av_packet_unref(AVPacket *pkt)
{
av_packet_free_side_data(pkt);
av_buffer_unref(&pkt->buf);
+ av_frame_free(&pkt->hw_frame);
av_init_packet(pkt);
pkt->data = NULL;
pkt->size = 0;
@@ -626,6 +628,20 @@ int av_packet_ref(AVPacket *dst, const AVPacket *src)
ret = AVERROR(ENOMEM);
goto fail;
}
+ if (src->hw_frame) {
+ dst->hw_frame = av_frame_alloc();
+ if (!dst->hw_frame) {
+ av_buffer_unref(&dst->buf);
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ ret = av_frame_ref(dst->hw_frame, src->hw_frame);
+ if (ret < 0) {
+ av_buffer_unref(&dst->buf);
+ av_frame_free(&dst->hw_frame);
+ goto fail;
+ }
+ }
dst->data = src->data;
}
diff --git a/libavcodec/rawdec.c b/libavcodec/rawdec.c
index 53f5b76..d6f1f81 100644
--- a/libavcodec/rawdec.c
+++ b/libavcodec/rawdec.c
@@ -73,37 +73,39 @@ static av_cold int raw_init_decoder(AVCodecContext *avctx)
ff_bswapdsp_init(&context->bbdsp);
- if ( avctx->codec_tag == MKTAG('r','a','w',' ')
- || avctx->codec_tag == MKTAG('N','O','1','6'))
- avctx->pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_mov,
- avctx->bits_per_coded_sample);
- else if (avctx->codec_tag == MKTAG('W', 'R', 'A', 'W'))
- avctx->pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi,
- avctx->bits_per_coded_sample);
- else if (avctx->codec_tag && (avctx->codec_tag & 0xFFFFFF) != MKTAG('B','I','T', 0))
- avctx->pix_fmt = avpriv_find_pix_fmt(ff_raw_pix_fmt_tags, avctx->codec_tag);
- else if (avctx->pix_fmt == AV_PIX_FMT_NONE && avctx->bits_per_coded_sample)
- avctx->pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi,
- avctx->bits_per_coded_sample);
-
- desc = av_pix_fmt_desc_get(avctx->pix_fmt);
- if (!desc) {
- av_log(avctx, AV_LOG_ERROR, "Invalid pixel format.\n");
- return AVERROR(EINVAL);
- }
+ if (!(avctx->codec_tag == MKTAG('I', 'G', 'N', 'R'))) {
+ if ( avctx->codec_tag == MKTAG('r','a','w',' ')
+ || avctx->codec_tag == MKTAG('N','O','1','6'))
+ avctx->pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_mov,
+ avctx->bits_per_coded_sample);
+ else if (avctx->codec_tag == MKTAG('W', 'R', 'A', 'W'))
+ avctx->pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi,
+ avctx->bits_per_coded_sample);
+ else if (avctx->codec_tag && (avctx->codec_tag & 0xFFFFFF) != MKTAG('B','I','T', 0))
+ avctx->pix_fmt = avpriv_find_pix_fmt(ff_raw_pix_fmt_tags, avctx->codec_tag);
+ else if (avctx->pix_fmt == AV_PIX_FMT_NONE && avctx->bits_per_coded_sample)
+ avctx->pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi,
+ avctx->bits_per_coded_sample);
+
+ desc = av_pix_fmt_desc_get(avctx->pix_fmt);
+ if (!desc) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid pixel format.\n");
+ return AVERROR(EINVAL);
+ }
- if (desc->flags & (AV_PIX_FMT_FLAG_PAL | FF_PSEUDOPAL)) {
- context->palette = av_buffer_alloc(AVPALETTE_SIZE);
- if (!context->palette)
- return AVERROR(ENOMEM);
-#if FF_API_PSEUDOPAL
- if (desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL)
- avpriv_set_systematic_pal2((uint32_t*)context->palette->data, avctx->pix_fmt);
-#endif
- else {
- memset(context->palette->data, 0, AVPALETTE_SIZE);
- if (avctx->bits_per_coded_sample == 1)
- memset(context->palette->data, 0xff, 4);
+ if (desc->flags & (AV_PIX_FMT_FLAG_PAL | FF_PSEUDOPAL)) {
+ context->palette = av_buffer_alloc(AVPALETTE_SIZE);
+ if (!context->palette)
+ return AVERROR(ENOMEM);
+ #if FF_API_PSEUDOPAL
+ if (desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL)
+ avpriv_set_systematic_pal2((uint32_t*)context->palette->data, avctx->pix_fmt);
+ #endif
+ else {
+ memset(context->palette->data, 0, AVPALETTE_SIZE);
+ if (avctx->bits_per_coded_sample == 1)
+ memset(context->palette->data, 0xff, 4);
+ }
}
}
@@ -179,7 +181,7 @@ static int raw_decode(AVCodecContext *avctx, void *data, int *got_frame,
int res, len;
int need_copy;
- AVFrame *frame = data;
+ AVFrame *frame = avpkt->hw_frame? avpkt->hw_frame : data;
if (avctx->width <= 0) {
av_log(avctx, AV_LOG_ERROR, "width is not set\n");
@@ -204,33 +206,35 @@ static int raw_decode(AVCodecContext *avctx, void *data, int *got_frame,
return AVERROR_INVALIDDATA;
}
- desc = av_pix_fmt_desc_get(avctx->pix_fmt);
-
- if ((avctx->bits_per_coded_sample == 8 || avctx->bits_per_coded_sample == 4 ||
- avctx->bits_per_coded_sample == 2 || avctx->bits_per_coded_sample == 1 ||
- (avctx->bits_per_coded_sample == 0 && (context->is_nut_pal8 || context->is_mono)) ) &&
- (context->is_mono || context->is_pal8) &&
- (!avctx->codec_tag || avctx->codec_tag == MKTAG('r','a','w',' ') ||
- context->is_nut_mono || context->is_nut_pal8)) {
- context->is_1_2_4_8_bpp = 1;
- if (context->is_mono) {
- int row_bytes = avctx->width / 8 + (avctx->width & 7 ? 1 : 0);
- context->frame_size = av_image_get_buffer_size(avctx->pix_fmt,
- FFALIGN(row_bytes, 16) * 8,
- avctx->height, 1);
- } else
- context->frame_size = av_image_get_buffer_size(avctx->pix_fmt,
- FFALIGN(avctx->width, 16),
- avctx->height, 1);
- } else {
- context->is_lt_16bpp = av_get_bits_per_pixel(desc) == 16 && avctx->bits_per_coded_sample && avctx->bits_per_coded_sample < 16;
- context->frame_size = av_image_get_buffer_size(avctx->pix_fmt, avctx->width,
- avctx->height, 1);
- }
- if (context->frame_size < 0)
- return context->frame_size;
+ if (!avpkt->hw_frame) {
+ desc = av_pix_fmt_desc_get(avctx->pix_fmt);
+
+ if ((avctx->bits_per_coded_sample == 8 || avctx->bits_per_coded_sample == 4 ||
+ avctx->bits_per_coded_sample == 2 || avctx->bits_per_coded_sample == 1 ||
+ (avctx->bits_per_coded_sample == 0 && (context->is_nut_pal8 || context->is_mono)) ) &&
+ (context->is_mono || context->is_pal8) &&
+ (!avctx->codec_tag || avctx->codec_tag == MKTAG('r','a','w',' ') ||
+ context->is_nut_mono || context->is_nut_pal8)) {
+ context->is_1_2_4_8_bpp = 1;
+ if (context->is_mono) {
+ int row_bytes = avctx->width / 8 + (avctx->width & 7 ? 1 : 0);
+ context->frame_size = av_image_get_buffer_size(avctx->pix_fmt,
+ FFALIGN(row_bytes, 16) * 8,
+ avctx->height, 1);
+ } else
+ context->frame_size = av_image_get_buffer_size(avctx->pix_fmt,
+ FFALIGN(avctx->width, 16),
+ avctx->height, 1);
+ } else {
+ context->is_lt_16bpp = av_get_bits_per_pixel(desc) == 16 && avctx->bits_per_coded_sample && avctx->bits_per_coded_sample < 16;
+ context->frame_size = av_image_get_buffer_size(avctx->pix_fmt, avctx->width,
+ avctx->height, 1);
+ }
+ if (context->frame_size < 0)
+ return context->frame_size;
- need_copy = !avpkt->buf || context->is_1_2_4_8_bpp || context->is_yuv2 || context->is_lt_16bpp;
+ need_copy = !avpkt->buf || context->is_1_2_4_8_bpp || context->is_yuv2 || context->is_lt_16bpp;
+ }
frame->pict_type = AV_PICTURE_TYPE_I;
frame->key_frame = 1;
@@ -250,6 +254,24 @@ static int raw_decode(AVCodecContext *avctx, void *data, int *got_frame,
if ((res = av_image_check_size(avctx->width, avctx->height, 0, avctx)) < 0)
return res;
+ if (avpkt->hw_frame) {
+ AVFrame *out_frame = data;
+
+ res = av_frame_ref(out_frame, avpkt->hw_frame);
+ if (res)
+ return res;
+
+ out_frame->buf[1] = av_buffer_ref(avpkt->buf);
+ if (!out_frame->buf[1]) {
+ av_frame_unref(out_frame);
+ return AVERROR(ENOMEM);
+ }
+ if (context->flip)
+ flip(avctx, out_frame);
+ frame = out_frame;
+ goto out;
+ }
+
if (need_copy)
frame->buf[0] = av_buffer_alloc(FFMAX(context->frame_size, buf_size));
else
@@ -474,6 +496,7 @@ static int raw_decode(AVCodecContext *avctx, void *data, int *got_frame,
}
}
+out:
if (avctx->field_order > AV_FIELD_PROGRESSIVE) { /* we have interlaced material flagged in container */
frame->interlaced_frame = 1;
if (avctx->field_order == AV_FIELD_TT || avctx->field_order == AV_FIELD_TB)
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index 1661d48..9ead54b 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -1204,22 +1204,33 @@ void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
", %d reference frame%s",
enc->refs, enc->refs > 1 ? "s" : "");
- if (enc->codec_tag)
+ if (enc->codec_tag) {
+ unsigned int tag = enc->codec_tag;
+ if (enc->codec_tag == MKTAG('I', 'G', 'N', 'R') &&
+ enc->sw_pix_fmt != AV_PIX_FMT_NONE)
+ tag = avcodec_pix_fmt_to_codec_tag(enc->sw_pix_fmt);
snprintf(buf + strlen(buf), buf_size - strlen(buf), " (%s / 0x%04X)",
- av_fourcc2str(enc->codec_tag), enc->codec_tag);
+ av_fourcc2str(tag), tag);
+ }
switch (enc->codec_type) {
case AVMEDIA_TYPE_VIDEO:
{
char detail[256] = "(";
+ enum AVPixelFormat pix_fmt = enc->pix_fmt;
av_strlcat(buf, separator, buf_size);
snprintf(buf + strlen(buf), buf_size - strlen(buf),
- "%s", enc->pix_fmt == AV_PIX_FMT_NONE ? "none" :
- av_get_pix_fmt_name(enc->pix_fmt));
- if (enc->bits_per_raw_sample && enc->pix_fmt != AV_PIX_FMT_NONE &&
- enc->bits_per_raw_sample < av_pix_fmt_desc_get(enc->pix_fmt)->comp[0].depth)
+ "%s", pix_fmt == AV_PIX_FMT_NONE ? "none" :
+ av_get_pix_fmt_name(pix_fmt));
+ if (enc->sw_pix_fmt != AV_PIX_FMT_NONE) {
+ pix_fmt = enc->sw_pix_fmt;
+ snprintf(buf + strlen(buf), buf_size - strlen(buf),
+ "(%s) ", av_get_pix_fmt_name(pix_fmt));
+ }
+ if (enc->bits_per_raw_sample && pix_fmt != AV_PIX_FMT_NONE &&
+ enc->bits_per_raw_sample < av_pix_fmt_desc_get(pix_fmt)->comp[0].depth)
av_strlcatf(detail, sizeof(detail), "%d bpc, ", enc->bits_per_raw_sample);
if (enc->color_range != AVCOL_RANGE_UNSPECIFIED)
av_strlcatf(detail, sizeof(detail), "%s, ",
@@ -1978,6 +1989,7 @@ static void codec_parameters_reset(AVCodecParameters *par)
par->codec_type = AVMEDIA_TYPE_UNKNOWN;
par->codec_id = AV_CODEC_ID_NONE;
par->format = -1;
+ par->sw_format = AV_PIX_FMT_NONE;
par->field_order = AV_FIELD_UNKNOWN;
par->color_range = AVCOL_RANGE_UNSPECIFIED;
par->color_primaries = AVCOL_PRI_UNSPECIFIED;
@@ -2046,6 +2058,7 @@ int avcodec_parameters_from_context(AVCodecParameters *par,
switch (par->codec_type) {
case AVMEDIA_TYPE_VIDEO:
par->format = codec->pix_fmt;
+ par->sw_format = codec->sw_pix_fmt;
par->width = codec->width;
par->height = codec->height;
par->field_order = codec->field_order;
@@ -2101,6 +2114,7 @@ int avcodec_parameters_to_context(AVCodecContext *codec,
switch (par->codec_type) {
case AVMEDIA_TYPE_VIDEO:
codec->pix_fmt = par->format;
+ codec->sw_pix_fmt = par->sw_format;
codec->width = par->width;
codec->height = par->height;
codec->field_order = par->field_order;
@@ -2197,7 +2211,9 @@ int64_t ff_guess_coded_bitrate(AVCodecContext *avctx)
return 0;
if (!bits_per_coded_sample) {
- const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt);
+ enum AVPixelFormat pix_fmt = avctx->sw_pix_fmt == AV_PIX_FMT_NONE ?
+ avctx->pix_fmt : avctx->sw_pix_fmt;
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
bits_per_coded_sample = av_get_bits_per_pixel(desc);
}
bitrate = (int64_t)bits_per_coded_sample * avctx->width * avctx->height *
diff --git a/libavdevice/v4l2.c b/libavdevice/v4l2.c
index 10a0ff0..d42abf5 100644
--- a/libavdevice/v4l2.c
+++ b/libavdevice/v4l2.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2000,2001 Fabrice Bellard
* Copyright (c) 2006 Luca Abeni
+ * Copyright (c) 2018 Hertz Wang
*
* This file is part of FFmpeg.
*
@@ -39,7 +40,11 @@
#include <libv4l2.h>
#endif
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_drm.h"
+
static const int desired_video_buffers = 256;
+static const int desired_drm_buffers = 4;
#define V4L_ALLFORMATS 3
#define V4L_RAWFORMATS 1
@@ -79,6 +84,8 @@ struct video_data {
TimeFilter *timefilter;
int64_t last_time_m;
+ enum v4l2_memory memory_type;
+
int buffers;
atomic_int buffers_queued;
void **buf_start;
@@ -91,6 +98,12 @@ struct video_data {
int list_standard; /**< Set by a private option. */
char *framerate; /**< Set by a private option. */
+ enum AVPixelFormat av_format;
+ AVBufferRef *hw_frames_ref;
+ AVFrame **pixel_frames;
+ int (*stream_init)(AVFormatContext *ctx);
+ void (*stream_close)(struct video_data *s);
+
int use_libv4l2;
int (*open_f)(const char *file, int oflag, ...);
int (*close_f)(int fd);
@@ -106,6 +119,9 @@ struct buff_data {
int index;
};
+static int mmap_init(AVFormatContext *ctx);
+static void mmap_close(struct video_data *s);
+
static int device_open(AVFormatContext *ctx, const char* device_path)
{
struct video_data *s = ctx->priv_data;
@@ -135,6 +151,11 @@ static int device_open(AVFormatContext *ctx, const char* device_path)
SET_WRAPPERS();
}
+ // default MMAP
+ s->memory_type = V4L2_MEMORY_MMAP;
+ s->stream_init = mmap_init;
+ s->stream_close = mmap_close;
+
#define v4l2_open s->open_f
#define v4l2_close s->close_f
#define v4l2_dup s->dup_f
@@ -396,6 +417,111 @@ static int mmap_init(AVFormatContext *ctx)
return 0;
}
+static void free_pixel_frames(struct video_data *s) {
+ int i;
+
+ for (i = 0; i < s->buffers; i++) {
+ if (s->pixel_frames[i])
+ av_frame_free(&s->pixel_frames[i]);
+ }
+ av_freep(&s->pixel_frames);
+}
+
+static int drm_init(AVFormatContext *ctx)
+{
+ int i, res;
+ struct video_data *s = ctx->priv_data;
+ AVHWFramesContext *hwframes = (AVHWFramesContext*)s->hw_frames_ref->data;
+
+ struct v4l2_requestbuffers req = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .count = desired_drm_buffers,
+ .memory = V4L2_MEMORY_DMABUF
+ };
+
+ if (v4l2_ioctl(s->fd, VIDIOC_REQBUFS, &req) < 0) {
+ res = AVERROR(errno);
+ av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_REQBUFS): %s\n", av_err2str(res));
+ return res;
+ }
+
+ s->memory_type = V4L2_MEMORY_DMABUF;
+
+ hwframes->format = AV_PIX_FMT_DRM_PRIME;
+ if (s->av_format != AV_PIX_FMT_NONE)
+ hwframes->sw_format = s->av_format;
+ else
+ hwframes->sw_format = AV_PIX_FMT_BGR0; // jpeg or h264
+ hwframes->width = s->width;
+ hwframes->height = s->height;
+ hwframes->initial_pool_size = req.count;
+ res = av_hwframe_ctx_init(s->hw_frames_ref);
+ if (res < 0)
+ return res;
+
+ if (hwframes->initial_pool_size < 2) {
+ av_log(ctx, AV_LOG_ERROR, "Insufficient buffer memory\n");
+ return AVERROR(ENOMEM);
+ }
+
+ s->buffers = hwframes->initial_pool_size;
+ s->pixel_frames = av_mallocz_array(s->buffers, sizeof(AVFrame *));
+ if (!s->pixel_frames) {
+ av_log(ctx, AV_LOG_ERROR, "Cannot allocate pixel frame pointers\n");
+ return AVERROR(ENOMEM);
+ }
+ s->buf_start = av_malloc_array(s->buffers, sizeof(void *));
+ if (!s->buf_start) {
+ av_log(ctx, AV_LOG_ERROR, "Cannot allocate buffer pointers\n");
+ res = AVERROR(ENOMEM);
+ goto fail;
+ }
+ s->buf_len = av_malloc_array(s->buffers, sizeof(unsigned int));
+ if (!s->buf_len) {
+ av_log(ctx, AV_LOG_ERROR, "Cannot allocate buffer sizes\n");
+ res = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ for (i = 0; i < s->buffers; i++) {
+ AVDRMFrameDescriptor *desc;
+ AVDRMObjectDescriptor *object;
+
+ s->pixel_frames[i] = av_frame_alloc();
+ if (!s->pixel_frames[i]) {
+ res = AVERROR(ENOMEM);
+ goto fail;
+ }
+ res = av_hwframe_get_buffer(s->hw_frames_ref, s->pixel_frames[i], 0);
+ if (res < 0)
+ goto fail;
+
+ desc = (AVDRMFrameDescriptor*)s->pixel_frames[i]->data[0];
+ object = &desc->objects[0];
+ // pool always provide only one object, and mmapped userspace viraddr
+ av_assert0(desc->nb_objects == 1 && desc->objects[0].ptr);
+
+ s->buf_start[i] = object->ptr;
+ s->buf_len[i] = object->size;
+ if (s->frame_size > 0 && s->buf_len[i] < s->frame_size) {
+ av_log(ctx, AV_LOG_ERROR,
+ "buf_len[%d] = %d < expected frame size %d\n",
+ i, s->buf_len[i], s->frame_size);
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ return 0;
+
+fail:
+ free_pixel_frames(s);
+ if (s->buf_start)
+ av_freep(&s->buf_start);
+ if (s->buf_len)
+ av_freep(&s->buf_len);
+ return res;
+}
+
static int enqueue_buffer(struct video_data *s, struct v4l2_buffer *buf)
{
int res = 0;
@@ -410,16 +536,22 @@ static int enqueue_buffer(struct video_data *s, struct v4l2_buffer *buf)
return res;
}
-static void mmap_release_buffer(void *opaque, uint8_t *data)
+static void stream_release_buffer(void *opaque, uint8_t *data)
{
struct v4l2_buffer buf = { 0 };
struct buff_data *buf_descriptor = opaque;
struct video_data *s = buf_descriptor->s;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
+ buf.memory = s->memory_type;
buf.index = buf_descriptor->index;
av_free(buf_descriptor);
+ if (s->hw_frames_ref) {
+ // V4L2_MEMORY_DMABUF
+ AVFrame *frame = s->pixel_frames[buf.index];
+ buf.m.fd = ((AVDRMFrameDescriptor*)frame->data[0])->objects[0].fd;
+ buf.length = s->buf_len[buf.index];
+ }
enqueue_buffer(s, &buf);
}
@@ -485,12 +617,12 @@ static int convert_timestamp(AVFormatContext *ctx, int64_t *ts)
return 0;
}
-static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt)
+static int stream_read_frame(AVFormatContext *ctx, AVPacket *pkt)
{
struct video_data *s = ctx->priv_data;
struct v4l2_buffer buf = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .memory = V4L2_MEMORY_MMAP
+ .memory = s->memory_type
};
struct timeval buf_ts;
int res;
@@ -543,7 +675,8 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt)
}
/* Image is at s->buff_start[buf.index] */
- if (atomic_load(&s->buffers_queued) == FFMAX(s->buffers / 8, 1)) {
+ if (s->memory_type == V4L2_MEMORY_MMAP &&
+ atomic_load(&s->buffers_queued) == FFMAX(s->buffers / 8, 1)) {
/* when we start getting low on queued buffers, fall back on copying data */
res = av_new_packet(pkt, buf.bytesused);
if (res < 0) {
@@ -577,11 +710,27 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt)
buf_descriptor->index = buf.index;
buf_descriptor->s = s;
- pkt->buf = av_buffer_create(pkt->data, pkt->size, mmap_release_buffer,
+ if (s->hw_frames_ref) {
+ if (pkt->hw_frame) {
+ av_log(ctx, AV_LOG_WARNING, "Input packet is not a empty packet!\n");
+ av_frame_free(&pkt->hw_frame);
+ }
+ pkt->hw_frame = av_frame_alloc();
+ if (!pkt->hw_frame ||
+ av_frame_ref(pkt->hw_frame, s->pixel_frames[buf.index])) {
+ enqueue_buffer(s, &buf);
+ av_frame_free(&pkt->hw_frame);
+ av_freep(&buf_descriptor);
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ pkt->buf = av_buffer_create(pkt->data, pkt->size, stream_release_buffer,
buf_descriptor, 0);
if (!pkt->buf) {
av_log(ctx, AV_LOG_ERROR, "Failed to create a buffer\n");
enqueue_buffer(s, &buf);
+ av_frame_free(&pkt->hw_frame);
av_freep(&buf_descriptor);
return AVERROR(ENOMEM);
}
@@ -592,7 +741,7 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt)
return pkt->size;
}
-static int mmap_start(AVFormatContext *ctx)
+static int stream_start(AVFormatContext *ctx)
{
struct video_data *s = ctx->priv_data;
enum v4l2_buf_type type;
@@ -602,9 +751,16 @@ static int mmap_start(AVFormatContext *ctx)
struct v4l2_buffer buf = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.index = i,
- .memory = V4L2_MEMORY_MMAP
+ .memory = s->memory_type
};
+ if (s->hw_frames_ref) {
+ // V4L2_MEMORY_DMABUF
+ AVFrame *frame = s->pixel_frames[i];
+ buf.m.fd = ((AVDRMFrameDescriptor*)frame->data[0])->objects[0].fd;
+ buf.length = s->buf_len[i];
+ }
+
if (v4l2_ioctl(s->fd, VIDIOC_QBUF, &buf) < 0) {
res = AVERROR(errno);
av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF): %s\n",
@@ -642,6 +798,19 @@ static void mmap_close(struct video_data *s)
av_freep(&s->buf_len);
}
+static void drm_close(struct video_data *s)
+{
+ enum v4l2_buf_type type;
+
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == v4l2_ioctl(s->fd, VIDIOC_STREAMOFF, &type))
+ av_log(NULL, AV_LOG_FATAL, "V4L2 Could not steam off (errno = %d).\n",
+ errno);
+ free_pixel_frames(s);
+ av_freep(&s->buf_start);
+ av_freep(&s->buf_len);
+}
+
static int v4l2_set_parameters(AVFormatContext *ctx)
{
struct video_data *s = ctx->priv_data;
@@ -887,8 +1056,37 @@ static int v4l2_read_header(AVFormatContext *ctx)
avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
if (s->pixel_format) {
- const AVCodecDescriptor *desc = avcodec_descriptor_get_by_name(s->pixel_format);
+ const AVCodecDescriptor *desc;
+ const char *ff_pix_fmt = NULL;
+
+ // drm_prime:ff_pix_fmt
+ if (av_strstart(s->pixel_format, "drm_prime:", &ff_pix_fmt)) {
+ // if set drm_prime, alloc drm buffer externally and queue to v4l2
+ AVBufferRef *hw_device_ref;
+ // libv4l2 has implicit format conversion,
+ // conflict to our intention of using drm external buffer
+ if (s->use_libv4l2) {
+ av_log(ctx, AV_LOG_WARNING,
+ "use_libv4l2 conflict to pixel format drm_prime\n");
+ res = AVERROR_EXIT;
+ goto fail;
+ }
+ res = av_hwdevice_ctx_create(&hw_device_ref, AV_HWDEVICE_TYPE_DRM,
+ NULL, NULL, 0);
+ if (res < 0)
+ goto fail;
+ s->hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ref);
+ av_buffer_unref(&hw_device_ref);
+ if (!s->hw_frames_ref) {
+ res = AVERROR(ENOMEM);
+ goto fail;
+ }
+ memmove(s->pixel_format, ff_pix_fmt, strlen(ff_pix_fmt) + 1);
+ s->stream_init = drm_init;
+ s->stream_close = drm_close;
+ }
+ desc = avcodec_descriptor_get_by_name(s->pixel_format);
if (desc)
ctx->video_codec_id = desc->id;
@@ -940,29 +1138,39 @@ static int v4l2_read_header(AVFormatContext *ctx)
if ((res = v4l2_set_parameters(ctx)) < 0)
goto fail;
+ s->av_format =
st->codecpar->format = ff_fmt_v4l2ff(desired_format, codec_id);
if (st->codecpar->format != AV_PIX_FMT_NONE)
s->frame_size = av_image_get_buffer_size(st->codecpar->format,
s->width, s->height, 1);
- if ((res = mmap_init(ctx)) ||
- (res = mmap_start(ctx)) < 0)
+ if ((res = s->stream_init(ctx)) ||
+ (res = stream_start(ctx)) < 0)
goto fail;
s->top_field_first = first_field(s);
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = codec_id;
- if (codec_id == AV_CODEC_ID_RAWVIDEO)
+ if (codec_id == AV_CODEC_ID_RAWVIDEO) {
st->codecpar->codec_tag =
avcodec_pix_fmt_to_codec_tag(st->codecpar->format);
- else if (codec_id == AV_CODEC_ID_H264) {
+ } else if (codec_id == AV_CODEC_ID_H264) {
st->need_parsing = AVSTREAM_PARSE_FULL_ONCE;
}
if (desired_format == V4L2_PIX_FMT_YVU420)
st->codecpar->codec_tag = MKTAG('Y', 'V', '1', '2');
else if (desired_format == V4L2_PIX_FMT_YVU410)
st->codecpar->codec_tag = MKTAG('Y', 'V', 'U', '9');
+
+ if (s->hw_frames_ref) {
+ // tag: ignore
+ st->codecpar->codec_tag = MKTAG('I', 'G', 'N', 'R');
+ st->codecpar->sw_format =
+ st->internal->avctx->sw_pix_fmt = st->codecpar->format;
+ st->codecpar->format = AV_PIX_FMT_DRM_PRIME;
+ }
+
st->codecpar->width = s->width;
st->codecpar->height = s->height;
if (st->avg_frame_rate.den)
@@ -971,6 +1179,8 @@ static int v4l2_read_header(AVFormatContext *ctx)
return 0;
fail:
+ if (s->hw_frames_ref)
+ av_buffer_unref(&s->hw_frames_ref);
v4l2_close(s->fd);
return res;
}
@@ -985,9 +1195,8 @@ FF_ENABLE_DEPRECATION_WARNINGS
#endif
int res;
- if ((res = mmap_read_frame(ctx, pkt)) < 0) {
+ if ((res = stream_read_frame(ctx, pkt)) < 0)
return res;
- }
#if FF_API_CODED_FRAME && FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
@@ -1009,7 +1218,10 @@ static int v4l2_read_close(AVFormatContext *ctx)
av_log(ctx, AV_LOG_WARNING, "Some buffers are still owned by the caller on "
"close.\n");
- mmap_close(s);
+ s->stream_close(s);
+
+ if (s->hw_frames_ref)
+ av_buffer_unref(&s->hw_frames_ref);
v4l2_close(s->fd);
return 0;
--
2.7.4