blob: 96d197e647b9a81e23e28f875525ac1516a06e95 [file] [log] [blame]
/*
* Copyright (c) 2013-2014, Freescale Semiconductor, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <linux/fb.h>
#include <linux/mxcfb.h>
#include <linux/ipu.h>
#include <linux/mxc_v4l2.h>
#include <linux/version.h>
#include <gst/video/gstvideosink.h>
#include "gstimxcommon.h"
#include "gstimxv4l2.h"
#define RGB888TORGB565(rgb)\
((((rgb)<<8)>>27<<11)|(((rgb)<<18)>>26<<5)|(((rgb)<<27)>>27))
#define RGB565TOCOLORKEY(rgb) \
( ((rgb & 0xf800)<<8) | ((rgb & 0xe000)<<3) | \
((rgb & 0x07e0)<<5) | ((rgb & 0x0600)>>1) | \
((rgb & 0x001f)<<3) | ((rgb & 0x001c)>>2) )
//used in imx v4l2 core debug
GST_DEBUG_CATEGORY (imxv4l2_debug);
#define GST_CAT_DEFAULT imxv4l2_debug
#define V4L2_HOLDED_BUFFERS (2)
#define MX6Q_STREAMON_COUNT (1)
#define MX60_STREAMON_COUNT (1)
#define MAX_BUFFER (32)
#define UPALIGNTO8(a) ((a + 7) & (~7))
#define DOWNALIGNTO8(a) ((a) & (~7))
#define INVISIBLE_IN (0x1)
#define INVISIBLE_OUT (0x2)
#define MXC_V4L2_CAPTURE_NAME "mxc_v4l2"
#define MXC_V4L2_CAPTURE_CAMERA_NAME "ov"
#define MXC_V4L2_CAPTURE_TVIN_NAME "adv"
#define MXC_V4L2_CAPTURE_TVIN_VADC_NAME "vadc"
#define PXP_V4L2_CAPTURE_NAME "csi_v4l2"
typedef struct {
struct v4l2_buffer v4l2buffer;
PhyMemBlock *v4l2memblk;
GstBuffer *gstbuffer;
guint8 *vaddr;
} IMXV4l2BufferPair;
typedef gint (*V4l2outConfigInput) (void *handle, guint fmt, guint w, guint h, \
IMXV4l2Rect *crop);
typedef gint (*V4l2outConfigOutput) (void *handle, struct v4l2_crop *crop);
typedef gint (*V4l2outConfigRotate) (void *handle, gint rotate);
typedef gint (*V4l2outConfigFlip) (void *handle, guint flip);
typedef gint (*V4l2outConfigAlpha) (void *handle, guint alpha);
typedef gint (*V4l2outConfigColorkey) (void *handle, gboolean enable, guint color_key);
typedef gint (*V4l2captureConfig) (void *handle, guint fmt, guint w, guint h, \
guint fps_n, guint fps_d);
static GstCaps *gst_imx_v4l2capture_get_device_caps ();
typedef struct {
V4l2outConfigInput v4l2out_config_input;
V4l2outConfigOutput v4l2out_config_output;
V4l2outConfigRotate v4l2out_config_rotate;
V4l2outConfigFlip v4l2out_config_flip;
V4l2outConfigAlpha v4l2out_config_alpha;
V4l2outConfigColorkey v4l2out_config_colorkey;
V4l2captureConfig v4l2capture_config;
} IMXV4l2DeviceItf;
typedef struct {
gchar *device;
gint type;
int v4l2_fd;
gint disp_w;
gint disp_h;
gint device_map_id;
gboolean streamon;
gint invisible;
gint streamon_count;
gint queued_count;
guint v4l2_hold_buf_num;
guint in_fmt;
gint in_w;
gint in_h;
IMXV4l2Rect in_crop;
gboolean do_deinterlace;
gint buffer_count;
guint memory_mode;
gint allocated;
IMXV4l2BufferPair buffer_pair[MAX_BUFFER];
gint rotate;
guint flip;
guint *support_format_table;
gboolean is_tvin;
IMXV4l2DeviceItf dev_itf;
struct v4l2_buffer * v4lbuf_queued_before_streamon[MAX_BUFFER];
v4l2_std_id id;
gboolean prev_need_crop;
guint alpha;
guint color_key;
IMXV4l2Rect overlay;
gboolean pending_close;
gboolean invalid_paddr;
} IMXV4l2Handle;
typedef struct {
const gchar * caps_str;
guint v4l2fmt;
GstVideoFormat gstfmt;
guint bits_per_pixel;
guint flags;
} IMXV4l2FmtMap;
typedef struct {
const gchar *name;
gboolean bg;
const gchar *bg_fb_name;
} IMXV4l2DeviceMap;
#define GST_VIDEO_CAPS_MAKE_BAYER(format) \
"video/x-bayer, " \
"format = (string) " format ", " \
"width = " GST_VIDEO_SIZE_RANGE ", " \
"height = " GST_VIDEO_SIZE_RANGE ", " \
"framerate = " GST_VIDEO_FPS_RANGE
static IMXV4l2FmtMap g_imxv4l2fmt_maps_IPU[] = {
{GST_VIDEO_CAPS_MAKE("I420"), V4L2_PIX_FMT_YUV420, GST_VIDEO_FORMAT_I420, 12, 0},
{GST_VIDEO_CAPS_MAKE("YV12"), V4L2_PIX_FMT_YVU420, GST_VIDEO_FORMAT_YV12, 12, 0},
{GST_VIDEO_CAPS_MAKE("NV12"), V4L2_PIX_FMT_NV12, GST_VIDEO_FORMAT_NV12, 12, 0},
{GST_VIDEO_CAPS_MAKE("Y42B"), V4L2_PIX_FMT_YUV422P, GST_VIDEO_FORMAT_Y42B, 16, 0},
{GST_VIDEO_CAPS_MAKE("AYUV"), V4L2_PIX_FMT_YUV32, GST_VIDEO_FORMAT_AYUV, 32, 0},
{GST_VIDEO_CAPS_MAKE("Y444"), IPU_PIX_FMT_YUV444P, GST_VIDEO_FORMAT_Y444, 24, 0},
{GST_VIDEO_CAPS_MAKE("TNVP"), IPU_PIX_FMT_TILED_NV12, GST_VIDEO_FORMAT_UNKNOWN, 12, 0},
{GST_VIDEO_CAPS_MAKE("TNVF"), IPU_PIX_FMT_TILED_NV12F, GST_VIDEO_FORMAT_UNKNOWN, 12, 0},
{GST_VIDEO_CAPS_MAKE("UYVY"), V4L2_PIX_FMT_UYVY, GST_VIDEO_FORMAT_UYVY, 16, 0},
{GST_VIDEO_CAPS_MAKE("YUY2"), V4L2_PIX_FMT_YUYV, GST_VIDEO_FORMAT_YUY2, 16, 0},
{GST_VIDEO_CAPS_MAKE("RGBA"), V4L2_PIX_FMT_RGB32, GST_VIDEO_FORMAT_RGBA, 32, 0},
{GST_VIDEO_CAPS_MAKE("RGBx"), V4L2_PIX_FMT_RGB32, GST_VIDEO_FORMAT_RGBx, 32, 0},
{GST_VIDEO_CAPS_MAKE("BGRx"), V4L2_PIX_FMT_BGR32, GST_VIDEO_FORMAT_BGRx, 32, 0},
{GST_VIDEO_CAPS_MAKE("RGB"), V4L2_PIX_FMT_RGB24, GST_VIDEO_FORMAT_RGB, 24, 0},
{GST_VIDEO_CAPS_MAKE("BGR"), V4L2_PIX_FMT_BGR24, GST_VIDEO_FORMAT_BGR, 24, 0},
{GST_VIDEO_CAPS_MAKE("RGB16"), V4L2_PIX_FMT_RGB565, GST_VIDEO_FORMAT_RGB16, 16, 0},
};
static IMXV4l2FmtMap g_imxv4l2fmt_maps_PXP[] = {
{GST_VIDEO_CAPS_MAKE("I420"), V4L2_PIX_FMT_YUV420, GST_VIDEO_FORMAT_I420, 12, 0},
{GST_VIDEO_CAPS_MAKE("YV12"), V4L2_PIX_FMT_YVU420, GST_VIDEO_FORMAT_YV12, 12, 0},
{GST_VIDEO_CAPS_MAKE("NV12"), V4L2_PIX_FMT_NV12, GST_VIDEO_FORMAT_NV12, 12, 0},
{GST_VIDEO_CAPS_MAKE("Y42B"), V4L2_PIX_FMT_YUV422P, GST_VIDEO_FORMAT_Y42B, 16, 0},
/* GST_VIDEO_FORMAT_AYUV is packed 4:4:4 YUV with alpha channel (A0-Y0-U0-V0 ...)
* V4L2_PIX_FMT_YUV32 is 32 YUV-8-8-8-8
* V4L2 capture output on SX TV In and PXP output on Kernel 3.14 is 32 bits
* packed AYUV444 with 4 bytes reversed (V0-U0-Y0-A0...). A is 0 */
{GST_VIDEO_CAPS_MAKE("AYUV"), V4L2_PIX_FMT_YUV32, GST_VIDEO_FORMAT_AYUV, 32, 0},
{GST_VIDEO_CAPS_MAKE("UYVY"), V4L2_PIX_FMT_UYVY, GST_VIDEO_FORMAT_UYVY, 16, 0},
{GST_VIDEO_CAPS_MAKE("YUY2"), V4L2_PIX_FMT_YUYV, GST_VIDEO_FORMAT_YUY2, 16, 0},
{GST_VIDEO_CAPS_MAKE("BGRx"), V4L2_PIX_FMT_RGB32, GST_VIDEO_FORMAT_BGRx, 32, 0},
//{GST_VIDEO_CAPS_MAKE("BGR"), V4L2_PIX_FMT_RGB24, GST_VIDEO_FORMAT_BGR, 24, 0},
{GST_VIDEO_CAPS_MAKE("RGB16"), V4L2_PIX_FMT_RGB565, GST_VIDEO_FORMAT_RGB16, 16, 0},
{GST_VIDEO_CAPS_MAKE_BAYER("bggr"), V4L2_PIX_FMT_SBGGR8, GST_VIDEO_FORMAT_UNKNOWN, 8, 0},
{GST_VIDEO_CAPS_MAKE_BAYER("gbrg"), V4L2_PIX_FMT_SGBRG8, GST_VIDEO_FORMAT_UNKNOWN, 8, 0},
{GST_VIDEO_CAPS_MAKE_BAYER("grbg"), V4L2_PIX_FMT_SGRBG8, GST_VIDEO_FORMAT_UNKNOWN, 8, 0},
{GST_VIDEO_CAPS_MAKE_BAYER("rggb"), V4L2_PIX_FMT_SRGGB8, GST_VIDEO_FORMAT_UNKNOWN, 8, 0},
};
static guint g_camera_format[] = {
V4L2_PIX_FMT_YUV420,
V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YUYV,
V4L2_PIX_FMT_UYVY,
0,
};
static guint g_camera_format_IPU[] = {
V4L2_PIX_FMT_YUV420,
V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YUYV,
V4L2_PIX_FMT_UYVY,
0,
};
static guint g_camera_format_PXP[] = {
V4L2_PIX_FMT_YUYV,
V4L2_PIX_FMT_UYVY,
0,
};
static IMXV4l2DeviceMap g_device_maps[] = {
{"/dev/video0", FALSE, "/dev/fb0"},
{"/dev/video16", TRUE, "/dev/fb0"},
{"/dev/video17", FALSE, "/dev/fb0"},
{"/dev/video18", TRUE, "/dev/fb2"},
{"/dev/video19", FALSE, "/dev/fb2"},
{"/dev/video20", TRUE, "/dev/fb4"}
};
static IMXV4l2FmtMap * imx_v4l2_get_fmt_map(guint *map_size)
{
IMXV4l2FmtMap *fmt_map = NULL;
*map_size = 0;
if (HAS_IPU()) {
fmt_map = g_imxv4l2fmt_maps_IPU;
*map_size = sizeof(g_imxv4l2fmt_maps_IPU)/sizeof(IMXV4l2FmtMap);
} else if (HAS_PXP()){
fmt_map = g_imxv4l2fmt_maps_PXP;
*map_size = sizeof(g_imxv4l2fmt_maps_PXP)/sizeof(IMXV4l2FmtMap);
}
return fmt_map;
}
//ipu device iterfaces
static gint
imx_ipu_v4l2out_config_input (IMXV4l2Handle *handle, guint fmt, guint w, guint h, IMXV4l2Rect *crop)
{
struct v4l2_format v4l2fmt;
if (gst_imx_v4l2_reset_device ((gpointer)handle) < 0)
return -1;
GST_DEBUG ("config in, fmt(%x), res(%dx%d), crop((%d,%d) -> (%d,%d))",
fmt, w, h, crop->left, crop->top, crop->width, crop->height);
//align to 8 pixel for IPU limitation
crop->left = UPALIGNTO8 (crop->left);
crop->top = UPALIGNTO8 (crop->top);
crop->width = DOWNALIGNTO8 (crop->width);
crop->height = DOWNALIGNTO8 (crop->height);
if (crop->width <=0 || crop->height <= 0) {
return 1;
}
memset(&v4l2fmt, 0, sizeof(struct v4l2_format));
v4l2fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
v4l2fmt.fmt.pix.width = handle->in_w = w;
v4l2fmt.fmt.pix.height = handle->in_h = h;
v4l2fmt.fmt.pix.pixelformat = handle->in_fmt = fmt;
/* v4l2 driver add VIDIOC_S_INPUT_CROP since 4.1 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
struct v4l2_rect icrop;
icrop.left = crop->left;
icrop.top = crop->top;
icrop.width = crop->width;
icrop.height = crop->height;
v4l2fmt.fmt.pix.priv = (unsigned int)&icrop;
#else
struct v4l2_crop icrop;
icrop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
icrop.c.top = crop->top;
icrop.c.left = crop->left;
if (crop->width && crop->height) {
icrop.c.width = crop->width;
icrop.c.height = crop->height;
} else {
icrop.c.width = w;
icrop.c.height = h;
}
if (ioctl(handle->v4l2_fd, VIDIOC_S_INPUT_CROP, &icrop) < 0) {
GST_ERROR("set icrop failed");
return -1;
}
#endif
if (ioctl(handle->v4l2_fd, VIDIOC_S_FMT, &v4l2fmt) < 0) {
GST_ERROR ("Set format failed.");
return -1;
}
if (ioctl(handle->v4l2_fd, VIDIOC_G_FMT, &v4l2fmt) < 0) {
GST_ERROR ("Get format failed.");
return -1;
}
return 0;
}
static gint
imx_ipu_v4l2out_config_output(IMXV4l2Handle *handle, struct v4l2_crop *crop)
{
struct v4l2_cropcap cropcap;
memset(&cropcap, 0, sizeof(cropcap));
cropcap.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
if (ioctl(handle->v4l2_fd, VIDIOC_CROPCAP, &cropcap) < 0) {
GST_ERROR ("Get crop capability failed.");
return -1;
}
crop->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
if (ioctl(handle->v4l2_fd, VIDIOC_S_CROP, crop) < 0) {
GST_ERROR ("Set crop failed.");
return -1;
}
return 0;
}
static gint
imx_ipu_v4l2out_config_rotate(IMXV4l2Handle *handle, gint rotate)
{
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_ROTATE;
ctrl.value = rotate;
if (ioctl(handle->v4l2_fd, VIDIOC_S_CTRL, &ctrl) < 0) {
GST_ERROR ("Set ctrl rotate failed.");
return -1;
}
return 0;
}
static void
imx_ipu_v4l2_config_alpha (IMXV4l2Handle *handle, guint alpha)
{
struct mxcfb_gbl_alpha galpha;
char *device = (char*) g_device_maps[handle->device_map_id].bg_fb_name;
int fd;
fd = open (device, O_RDWR, 0);
if (fd < 0) {
GST_ERROR ("Can't open %s.\n", device);
return;
}
GST_DEBUG ("set alpha to (%d) for display (%s)", alpha, device);
galpha.alpha = alpha;
galpha.enable = 1;
if (ioctl(fd, MXCFB_SET_GBL_ALPHA, &galpha) < 0) {
GST_WARNING ("Set %d global alpha failed.", alpha);
}
close (fd);
return;
}
static void
imx_ipu_v4l2_config_colorkey (IMXV4l2Handle *handle, gboolean enable, guint color_key)
{
struct mxcfb_color_key colorKey;
char *device = (char*)g_device_maps[handle->device_map_id].bg_fb_name;
int fd;
struct fb_var_screeninfo fbVar;
fd = open (device, O_RDWR, 0);
if (fd < 0) {
GST_ERROR ("Can't open %s.", device);
return;
}
if (ioctl(fd, FBIOGET_VSCREENINFO, &fbVar) < 0) {
GST_ERROR("get vscreen info failed.");
} else {
if (fbVar.bits_per_pixel == 16) {
colorKey.color_key = RGB565TOCOLORKEY(RGB888TORGB565(color_key));
GST_DEBUG("%08X:%08X", colorKey.color_key, color_key);
} else if (fbVar.bits_per_pixel == 24 || fbVar.bits_per_pixel == 32) {
colorKey.color_key = color_key;
}
GST_DEBUG ("set colorKey to (%x) for display (%s)", colorKey.color_key, device);
}
if (enable) {
colorKey.enable = 1;
GST_DEBUG ("enable colorKey for display (%s)", device);
}
else {
colorKey.enable = 0;
GST_DEBUG ("disable colorKey for display (%s)", device);
}
if (ioctl (fd, MXCFB_SET_CLR_KEY, &colorKey) < 0) {
GST_WARNING ("Set %s color key failed.", device);
}
close (fd);
}
//pxp device iterfaces
static gint
imx_pxp_v4l2out_config_input (IMXV4l2Handle *handle, guint fmt, guint w, guint h, IMXV4l2Rect *crop)
{
struct v4l2_format v4l2fmt;
struct v4l2_rect icrop;
int out_idx = 1;
if (gst_imx_v4l2_reset_device ((gpointer)handle) < 0)
return -1;
GST_DEBUG ("config in, fmt(%x), res(%dx%d), crop((%d,%d) -> (%d,%d))",
fmt, w, h, crop->left, crop->top, crop->width, crop->height);
if (crop->width <=0 || crop->height <= 0) {
return 1;
}
if (ioctl(handle->v4l2_fd, VIDIOC_S_OUTPUT, &out_idx) < 0) {
GST_ERROR("Set output failed.");
return -1;
}
memset(&v4l2fmt, 0, sizeof(struct v4l2_format));
v4l2fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
v4l2fmt.fmt.pix.width = handle->in_w = w;
v4l2fmt.fmt.pix.height = handle->in_h = h;
v4l2fmt.fmt.pix.pixelformat = handle->in_fmt = fmt;
if (ioctl(handle->v4l2_fd, VIDIOC_S_FMT, &v4l2fmt) < 0) {
GST_ERROR ("Set format failed.");
return -1;
}
if (ioctl(handle->v4l2_fd, VIDIOC_G_FMT, &v4l2fmt) < 0) {
GST_ERROR ("Get format failed.");
return -1;
}
if (v4l2fmt.fmt.pix.width < w || v4l2fmt.fmt.pix.height < h) {
GST_ERROR ("Resolution out of range %dx%d\n", w, h);
return -1;
}
/* Set overlay source window */
memset(&v4l2fmt, 0, sizeof(struct v4l2_format));
v4l2fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY;
v4l2fmt.fmt.win.global_alpha = handle->alpha;
v4l2fmt.fmt.win.chromakey = handle->color_key;
v4l2fmt.fmt.win.w.left = crop->left;
v4l2fmt.fmt.win.w.top = crop->top;
v4l2fmt.fmt.win.w.width = crop->width;
v4l2fmt.fmt.win.w.height = crop->height;
if (ioctl(handle->v4l2_fd, VIDIOC_S_FMT, &v4l2fmt) < 0) {
GST_ERROR ("Set VIDIOC_S_FMT output overlay failed.");
return -1;
}
return 0;
}
static gint
imx_pxp_v4l2out_config_output(IMXV4l2Handle *handle, struct v4l2_crop *crop)
{
crop->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY;
if (ioctl(handle->v4l2_fd, VIDIOC_S_CROP, crop) < 0) {
GST_ERROR ("Set crop failed.");
return -1;
}
return 0;
}
static gint
imx_pxp_v4l2out_config_rotate(IMXV4l2Handle *handle, gint rotate)
{
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_PRIVATE_BASE;
ctrl.value = rotate;
if (ioctl(handle->v4l2_fd, VIDIOC_S_CTRL, &ctrl) < 0) {
GST_ERROR ("Set ctrl rotate failed.");
return -1;
}
return 0;
}
static void
imx_pxp_v4l2_config_alpha (IMXV4l2Handle *handle, guint alpha)
{
struct v4l2_framebuffer fb;
GST_DEBUG ("set alpha to (%d)", alpha);
fb.flags = V4L2_FBUF_FLAG_OVERLAY;
fb.flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
if (ioctl(handle->v4l2_fd, VIDIOC_S_FBUF, &fb) < 0) {
GST_ERROR ("VIDIOC_S_FBUF failed.");
return;
}
handle->alpha = alpha;
struct v4l2_format v4l2fmt;
memset(&v4l2fmt, 0, sizeof(struct v4l2_format));
v4l2fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY;
v4l2fmt.fmt.win.global_alpha = handle->alpha;
v4l2fmt.fmt.win.chromakey = handle->color_key;
v4l2fmt.fmt.win.w.left = handle->in_crop.left;
v4l2fmt.fmt.win.w.top = handle->in_crop.top;
v4l2fmt.fmt.win.w.width = handle->in_crop.width;
v4l2fmt.fmt.win.w.height = handle->in_crop.height;
if (ioctl(handle->v4l2_fd, VIDIOC_S_FMT, &v4l2fmt) < 0)
GST_ERROR ("Set VIDIOC_S_FMT output overlay failed.");
return;
}
static void
imx_pxp_v4l2_config_colorkey (IMXV4l2Handle *handle, gboolean enable, guint color_key)
{
struct v4l2_framebuffer fb;
guint key = color_key;
char *device = (char*)g_device_maps[handle->device_map_id].bg_fb_name;
int fd;
struct fb_var_screeninfo fbVar;
fd = open (device, O_RDWR, 0);
if (fd < 0) {
GST_ERROR ("Can't open %s.", device);
return;
}
if (ioctl(fd, FBIOGET_VSCREENINFO, &fbVar) < 0) {
GST_ERROR("get vscreen info failed.");
close(fd);
return;
} else {
if (fbVar.bits_per_pixel == 16) {
key = RGB565TOCOLORKEY(RGB888TORGB565(color_key));
GST_DEBUG("%08X:%08X", key, color_key);
} else if (fbVar.bits_per_pixel == 24 || fbVar.bits_per_pixel == 32) {
key = color_key;
}
}
fb.flags = V4L2_FBUF_FLAG_OVERLAY;
if (enable) {
fb.flags |= V4L2_FBUF_FLAG_CHROMAKEY;
fb.flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
handle->color_key = key;
GST_DEBUG ("set colorKey to (%x) ", key);
} else {
fb.flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
}
if (ioctl(handle->v4l2_fd, VIDIOC_S_FBUF, &fb) < 0) {
GST_ERROR ("VIDIOC_S_FBUF failed.");
close(fd);
return;
}
struct v4l2_format v4l2fmt;
memset(&v4l2fmt, 0, sizeof(struct v4l2_format));
v4l2fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY;
v4l2fmt.fmt.win.global_alpha = handle->alpha;
v4l2fmt.fmt.win.chromakey = handle->color_key;
v4l2fmt.fmt.win.w.left = handle->in_crop.left;
v4l2fmt.fmt.win.w.top = handle->in_crop.top;
v4l2fmt.fmt.win.w.width = handle->in_crop.width;
v4l2fmt.fmt.win.w.height = handle->in_crop.height;
if (ioctl(handle->v4l2_fd, VIDIOC_S_FMT, &v4l2fmt) < 0)
GST_ERROR ("Set VIDIOC_S_FMT output overlay failed.");
close(fd);
}
/* this is a common interface for pxp and ipu */
static gint
imx_v4l2out_config_flip(IMXV4l2Handle *handle, guint flip)
{
struct v4l2_control ctrl;
ctrl.id = flip;
ctrl.value = 1;
if (ioctl(handle->v4l2_fd, VIDIOC_S_CTRL, &ctrl) < 0) {
GST_ERROR ("Set ctrl flip failed.");
return -1;
}
return 0;
}
gchar *
gst_imx_v4l2_get_default_device_name (gint type)
{
char * devname;
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
if (HAS_IPU())
devname = (char*)"/dev/video17";
else if (HAS_PXP())
devname = (char*)"/dev/video0";
else {
GST_ERROR ("UNKNOWN imx SoC.");
return NULL;
}
}
else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
devname = (char*)"/dev/video0";
}
else {
GST_ERROR ("UNKNOWN v4l2 type %d\n", type);
devname = NULL;
}
return devname;
}
gint
gst_imx_v4l2_get_min_buffer_num (gpointer *v4l2handle, gint type)
{
gint num = 0;
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
if (handle && type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
if (HAS_PXP())
num = MAX (handle->v4l2_hold_buf_num, MX60_STREAMON_COUNT);
else if (HAS_IPU())
num = MAX (handle->v4l2_hold_buf_num, MX6Q_STREAMON_COUNT);
else
num = handle->v4l2_hold_buf_num;
num += 1;
}
return num;
}
GstCaps *
gst_imx_v4l2_get_device_caps (gint type)
{
struct v4l2_fmtdesc fmtdesc;
char * devname;
int fd;
gint i;
GstCaps *caps = NULL;
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
return gst_imx_v4l2capture_get_device_caps();
}
devname = gst_imx_v4l2_get_default_device_name (type);
fd = open(devname, O_RDWR | O_NONBLOCK, 0);
if (fd < 0) {
GST_ERROR ("Can't open %s\n", devname);
return NULL;
}
fmtdesc.type = type;
fmtdesc.index = 0;
while (!(ioctl (fd, VIDIOC_ENUM_FMT, &fmtdesc))) {
guint map_size;
IMXV4l2FmtMap *fmt_map = imx_v4l2_get_fmt_map(&map_size);
for (i=0; i<map_size; i++) {
if (fmtdesc.pixelformat == fmt_map[i].v4l2fmt) {
if (!caps)
caps = gst_caps_new_empty ();
if (caps) {
GstStructure * structure = gst_structure_from_string(fmt_map[i].caps_str, NULL);
gst_caps_append_structure (caps, structure);
}
}
}
fmtdesc.index ++;
};
close (fd);
return caps;
}
GstCaps *
gst_imx_v4l2_get_caps (gpointer v4l2handle)
{
struct v4l2_fmtdesc fmt;
struct v4l2_frmsizeenum frmsize;
struct v4l2_frmivalenum frmival;
gint i, index, vformat;
GstCaps *caps = NULL;
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
if (handle->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
fmt.index = 0;
fmt.type = handle->type;
//FIXME: driver should report v4l2 capture output format. not camera sensor
//support format.
if (handle->support_format_table) {
while (handle->support_format_table[fmt.index]) {
fmt.pixelformat = handle->support_format_table[fmt.index];
vformat = fmt.pixelformat;
GST_INFO ("frame format: %c%c%c%c", vformat & 0xff, (vformat >> 8) & 0xff,
(vformat >> 16) & 0xff, (vformat >> 24) & 0xff);
frmsize.pixel_format = fmt.pixelformat;
frmsize.index = 0;
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) {
GST_INFO ("frame size: %dx%d", frmsize.discrete.width, frmsize.discrete.height);
GST_INFO ("frame size type: %d", frmsize.type);
//FIXME: driver haven't set type.
if (1) {//frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
frmival.index = 0;
frmival.pixel_format = fmt.pixelformat;
frmival.width = frmsize.discrete.width;
frmival.height = frmsize.discrete.height;
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) {
GST_INFO ("frame rate: %d/%d", frmival.discrete.denominator, frmival.discrete.numerator);
// Add hard code format.
index = 0;
while (handle->support_format_table[index]) {
guint map_size;
IMXV4l2FmtMap *fmt_map = imx_v4l2_get_fmt_map(&map_size);
for (i=0; i<map_size; i++) {
if (handle->support_format_table[index] == fmt_map[i].v4l2fmt) {
if (!caps)
caps = gst_caps_new_empty ();
if (caps) {
GstStructure * structure = gst_structure_from_string( \
fmt_map[i].caps_str, NULL);
gst_structure_set (structure, "width", G_TYPE_INT, frmsize.discrete.width, NULL);
gst_structure_set (structure, "height", G_TYPE_INT, frmsize.discrete.height, NULL);
gst_structure_set (structure, "framerate", GST_TYPE_FRACTION, \
frmival.discrete.denominator, frmival.discrete.numerator, NULL);
if (handle->is_tvin)
gst_structure_set (structure, "interlace-mode", G_TYPE_STRING, "interleaved", NULL);
gst_caps_append_structure (caps, structure);
GST_INFO ("Added one caps\n");
}
}
}
index ++;
}
frmival.index++;
}
}
frmsize.index++;
}
fmt.index++;
}
} else {
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FMT, &fmt) >= 0) {
vformat = fmt.pixelformat;
GST_INFO ("frame format: %c%c%c%c", vformat & 0xff, (vformat >> 8) & 0xff,
(vformat >> 16) & 0xff, (vformat >> 24) & 0xff);
frmsize.pixel_format = fmt.pixelformat;
frmsize.index = 0;
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) {
GST_INFO ("frame size: %dx%d", frmsize.discrete.width, frmsize.discrete.height);
GST_INFO ("frame size type: %d", frmsize.type);
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
frmival.index = 0;
frmival.pixel_format = fmt.pixelformat;
frmival.width = frmsize.discrete.width;
frmival.height = frmsize.discrete.height;
while (ioctl(handle->v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) {
GST_INFO ("frame rate: %d/%d", frmival.discrete.denominator, frmival.discrete.numerator);
guint map_size;
IMXV4l2FmtMap *fmt_map = imx_v4l2_get_fmt_map(&map_size);
for (i=0; i<map_size; i++) {
if (fmt.pixelformat == fmt_map[i].v4l2fmt) {
if (!caps)
caps = gst_caps_new_empty ();
if (caps) {
GstStructure * structure = gst_structure_from_string( \
fmt_map[i].caps_str, NULL);
gst_structure_set (structure, "width", G_TYPE_INT, frmsize.discrete.width, NULL);
gst_structure_set (structure, "height", G_TYPE_INT, frmsize.discrete.height, NULL);
gst_structure_set (structure, "framerate", GST_TYPE_FRACTION, \
frmival.discrete.denominator, frmival.discrete.numerator, NULL);
gst_caps_append_structure (caps, structure);
GST_INFO ("Added one caps\n");
}
}
}
frmival.index++;
}
}
frmsize.index++;
}
fmt.index++;
}
}
}
if (caps) {
return gst_caps_simplify(caps);
} else {
return NULL;
}
}
guint
gst_imx_v4l2_fmt_gst2v4l2 (GstVideoFormat gstfmt)
{
guint v4l2fmt = 0;
int i;
guint map_size;
IMXV4l2FmtMap *fmt_map = imx_v4l2_get_fmt_map(&map_size);
for(i=0; i<map_size; i++) {
if (gstfmt == fmt_map[i].gstfmt) {
v4l2fmt = fmt_map[i].v4l2fmt;
break;
}
}
return v4l2fmt;
}
guint
gst_imx_v4l2_get_bits_per_pixel (guint v4l2fmt)
{
guint bits_per_pixel = 0;
int i;
guint map_size;
IMXV4l2FmtMap *fmt_map = imx_v4l2_get_fmt_map(&map_size);
for(i=0; i<map_size; i++) {
if (v4l2fmt == fmt_map[i].v4l2fmt) {
bits_per_pixel = fmt_map[i].bits_per_pixel;
break;
}
}
return bits_per_pixel;
}
gboolean
gst_imx_v4l2_support_deinterlace (gint type)
{
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
if (HAS_IPU())
return TRUE;
else if (HAS_PXP())
return FALSE;
else {
GST_ERROR ("UNKNOWN imx SoC.");
return FALSE;
}
}
return FALSE;
}
static void
gst_imx_v4l2output_set_default_res (IMXV4l2Handle *handle)
{
struct fb_var_screeninfo fb_var;
IMXV4l2Rect rect;
gint i;
for (i=0; i<sizeof(g_device_maps)/sizeof(IMXV4l2DeviceMap); i++) {
if (!strcmp (handle->device, g_device_maps[i].name)) {
handle->device_map_id = i;
break;
}
}
gst_imx_v4l2_get_display_resolution (handle->device, &handle->disp_w, &handle->disp_h);
if (g_device_maps[handle->device_map_id].bg)
return;
//set gblobal alpha to 0 to show video
gst_imx_v4l2out_config_alpha (handle, 0);
rect.left = 0;
rect.top = 0;
rect.width = handle->disp_w;
rect.height = handle->disp_h;
gst_imx_v4l2out_config_output (handle, &rect, TRUE);
return;
}
static gint
gst_imx_v4l2capture_config_usb_camera (IMXV4l2Handle *handle, guint fmt, guint w, guint h, guint fps_n, guint fps_d)
{
struct v4l2_format v4l2_fmt = {0};
struct v4l2_frmsizeenum fszenum = {0};
struct v4l2_streamparm parm = {0};
gint capture_mode = -1;
fszenum.index = 0;
fszenum.pixel_format = fmt;
while (ioctl (handle->v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &fszenum) >= 0){
if (fszenum.discrete.width == w && fszenum.discrete.height == h) {
capture_mode = fszenum.index;
break;
}
fszenum.index ++;
}
if (capture_mode < 0) {
GST_ERROR ("can't support resolution.");
return -1;
}
GST_INFO ("capture mode %d: %dx%d", capture_mode, w, h);
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = fps_d;
parm.parm.capture.timeperframe.denominator = fps_n;
parm.parm.capture.capturemode = capture_mode;
if (ioctl (handle->v4l2_fd, VIDIOC_S_PARM, &parm) < 0) {
GST_ERROR ("VIDIOC_S_PARM failed");
return -1;
}
GST_INFO ("frame format: %c%c%c%c", fmt & 0xff, (fmt >> 8) & 0xff,
(fmt >> 16) & 0xff, (fmt >> 24) & 0xff);
v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_fmt.fmt.pix.pixelformat = fmt;
v4l2_fmt.fmt.pix.width = w;
v4l2_fmt.fmt.pix.height = h;
if (ioctl (handle->v4l2_fd, VIDIOC_S_FMT, &v4l2_fmt) < 0) {
GST_ERROR ("VIDIOC_S_FMT failed");
return -1;
}
return 0;
}
static gint
gst_imx_v4l2capture_config_pxp (IMXV4l2Handle *handle, guint fmt, guint w, guint h, guint fps_n, guint fps_d)
{
// can add crop process if needed.
if (gst_imx_v4l2capture_config_usb_camera (handle, fmt, w, h, fps_n, fps_d) < 0) {
GST_ERROR ("camera config failed\n");
return -1;
}
return 0;
}
static gint
gst_imx_v4l2capture_config_camera (IMXV4l2Handle *handle, guint fmt, guint w, guint h, guint fps_n, guint fps_d)
{
gint input = 1;
if (ioctl (handle->v4l2_fd, VIDIOC_S_INPUT, &input) < 0) {
GST_ERROR ("VIDIOC_S_INPUT failed");
return -1;
}
return gst_imx_v4l2capture_config_pxp (handle, fmt, w, h, fps_n, fps_d);
}
static gint
gst_imx_v4l2capture_config_tvin_std (IMXV4l2Handle *handle)
{
if (ioctl (handle->v4l2_fd, VIDIOC_G_STD, &handle->id) < 0) {
GST_ERROR ("VIDIOC_G_STD failed\n");
return -1;
}
if (ioctl (handle->v4l2_fd, VIDIOC_S_STD, &handle->id) < 0) {
GST_ERROR ("VIDIOC_S_STD failed\n");
return -1;
}
return 0;
}
static gint
gst_imx_v4l2capture_set_function (IMXV4l2Handle *handle)
{
struct v4l2_capability cap;
if (ioctl(handle->v4l2_fd, VIDIOC_QUERYCAP, &cap) < 0) {
GST_ERROR ("VIDIOC_QUERYCAP error.");
return -1;
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
GST_ERROR ("device can't capture.");
return -1;
}
handle->is_tvin = FALSE;
if (!strcmp (cap.driver, MXC_V4L2_CAPTURE_NAME)) {
struct v4l2_dbg_chip_ident chip;
if (ioctl(handle->v4l2_fd, VIDIOC_DBG_G_CHIP_IDENT, &chip)) {
GST_ERROR ("VIDIOC_DBG_G_CHIP_IDENT failed.\n");
return -1;
}
GST_INFO ("sensor chip is %s\n", chip.match.name);
if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_CAMERA_NAME, 2)) {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_camera;
handle->support_format_table = g_camera_format_IPU;
} else if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_TVIN_NAME, 3)) {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_camera;
handle->support_format_table = g_camera_format_IPU;
handle->is_tvin = TRUE;
if (gst_imx_v4l2capture_config_tvin_std (handle)) {
GST_ERROR ("can't set TV-In STD.\n");
return -1;
}
} else {
GST_ERROR ("can't identify capture sensor type.\n");
return -1;
}
} else if (!strcmp (cap.driver, PXP_V4L2_CAPTURE_NAME)) {
struct v4l2_dbg_chip_ident chip;
if (ioctl(handle->v4l2_fd, VIDIOC_DBG_G_CHIP_IDENT, &chip)) {
GST_ERROR ("VIDIOC_DBG_G_CHIP_IDENT failed.\n");
return -1;
}
GST_INFO ("sensor chip is %s\n", chip.match.name);
if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_CAMERA_NAME, 2)) {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_pxp;
handle->support_format_table = g_camera_format_PXP;
} else if (!strncmp (chip.match.name, MXC_V4L2_CAPTURE_TVIN_VADC_NAME, 3)) {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_pxp;
handle->support_format_table = g_camera_format_PXP;
handle->is_tvin = TRUE;
if (gst_imx_v4l2capture_config_tvin_std (handle)) {
GST_ERROR ("can't set TV-In STD.\n");
return -1;
}
} else {
GST_ERROR ("can't identify capture sensor type.\n");
return -1;
}
} else {
handle->dev_itf.v4l2capture_config = (V4l2captureConfig)gst_imx_v4l2capture_config_usb_camera;
handle->support_format_table = NULL;
}
return 0;
}
#define DEFAULTW (320)
#define DEFAULTH (240)
void gst_imx_v4l2_get_display_resolution (gchar *device, gint *w, gint *h)
{
struct fb_var_screeninfo fb_var;
gint i, device_map_id = -1;
int fd;
*w = DEFAULTW;
*h = DEFAULTH;
for (i=0; i<sizeof(g_device_maps)/sizeof(IMXV4l2DeviceMap); i++) {
if (!strcmp (device, g_device_maps[i].name)) {
device_map_id = i;
break;
}
}
if (device_map_id < 0) {
g_print ("ERROR: Can't find %s.\n", device);
return;
}
fd = open (g_device_maps[device_map_id].bg_fb_name, O_RDWR, 0);
if (fd < 0) {
g_print ("ERROR: Can't open %s.\n", g_device_maps[device_map_id].bg_fb_name);
return;
}
if (ioctl (fd, FBIOGET_VSCREENINFO, &fb_var) < 0) {
g_print ("ERROR: Can't get display resolution, use default (%dx%d).\n", DEFAULTW, DEFAULTH);
close (fd);
return;
}
*w = fb_var.xres;
*h = fb_var.yres;
g_print ("display(%s) resolution is (%dx%d).\n", g_device_maps[device_map_id].bg_fb_name, fb_var.xres, fb_var.yres);
close (fd);
return;
}
gpointer gst_imx_v4l2_open_device (gchar *device, int type)
{
int fd;
struct v4l2_capability cap;
IMXV4l2Handle *handle = NULL;
GST_DEBUG_CATEGORY_INIT (imxv4l2_debug, "imxv4l2", 0, "IMX V4L2 Core");
GST_INFO ("device name: %s", device);
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
fd = open(device, O_RDWR, 0);
} else {
fd = open(device, O_RDWR | O_NONBLOCK, 0);
}
if (fd < 0) {
GST_DEBUG ("Can't open %s.\n", device);
return NULL;
}
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
GST_ERROR ("VIDIOC_QUERYCAP error.");
close (fd);
return NULL;
}
if (!(cap.capabilities & type)) {
GST_DEBUG ("device can't capture.");
close (fd);
return NULL;
}
handle = (IMXV4l2Handle*) g_slice_alloc (sizeof(IMXV4l2Handle));
if (!handle) {
GST_ERROR ("allocate for IMXV4l2Handle failed.\n");
close (fd);
return NULL;
}
memset (handle, 0, sizeof(IMXV4l2Handle));
handle->v4l2_fd = fd;
handle->device = device;
handle->type = type;
handle->streamon = FALSE;
handle->v4l2_hold_buf_num = V4L2_HOLDED_BUFFERS;
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
if (HAS_IPU()) {
handle->dev_itf.v4l2out_config_input = (V4l2outConfigInput)imx_ipu_v4l2out_config_input;
handle->dev_itf.v4l2out_config_output = (V4l2outConfigOutput)imx_ipu_v4l2out_config_output;
handle->dev_itf.v4l2out_config_rotate = (V4l2outConfigRotate)imx_ipu_v4l2out_config_rotate;
handle->dev_itf.v4l2out_config_alpha = (V4l2outConfigAlpha) imx_ipu_v4l2_config_alpha;
handle->dev_itf.v4l2out_config_colorkey = (V4l2outConfigColorkey) imx_ipu_v4l2_config_colorkey;
handle->streamon_count = MX6Q_STREAMON_COUNT;
}
else if (HAS_PXP()) {
handle->dev_itf.v4l2out_config_input = (V4l2outConfigInput)imx_pxp_v4l2out_config_input;
handle->dev_itf.v4l2out_config_output = (V4l2outConfigOutput)imx_pxp_v4l2out_config_output;
handle->dev_itf.v4l2out_config_rotate = (V4l2outConfigRotate)imx_pxp_v4l2out_config_rotate;
handle->dev_itf.v4l2out_config_alpha = (V4l2outConfigAlpha) imx_pxp_v4l2_config_alpha;
handle->dev_itf.v4l2out_config_colorkey = (V4l2outConfigColorkey) imx_pxp_v4l2_config_colorkey;
handle->streamon_count = MX60_STREAMON_COUNT;
}
handle->dev_itf.v4l2out_config_flip = (V4l2outConfigFlip)imx_v4l2out_config_flip;
gst_imx_v4l2output_set_default_res (handle);
}
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
if (gst_imx_v4l2capture_set_function (handle) < 0) {
GST_ERROR ("v4l2 capture set function failed.\n");
close (fd);
return NULL;
}
handle->streamon_count = 2;
}
return (gpointer) handle;
}
gint gst_imx_v4l2_reset_device (gpointer v4l2handle)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
gint i;
if (handle && handle->v4l2_fd) {
if (handle->streamon) {
if (ioctl (handle->v4l2_fd, VIDIOC_STREAMOFF, &handle->type) < 0) {
GST_ERROR ("stream off failed\n");
return -1;
}
handle->streamon = FALSE;
GST_DEBUG ("V4L2 device is STREAMOFF.");
}
GST_DEBUG ("V4l2 device hold (%d) buffers when reset.", handle->queued_count);
for (i=0; i<handle->buffer_count; i++) {
GST_DEBUG ("unref v4l held gstbuffer(%p).", handle->buffer_pair[i].gstbuffer);
if (handle->buffer_pair[i].gstbuffer) {
gst_buffer_unref (handle->buffer_pair[i].gstbuffer);
handle->buffer_pair[i].gstbuffer = NULL;
}
}
handle->queued_count = 0;
}
return 0;
}
gint gst_imx_v4l2_close_device (gpointer v4l2handle)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
if (handle) {
/*set global alpha to 255 when quit in case of overlay is already in use and
* part is transparent to UI*/
if (HAS_IPU() && handle->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
gst_imx_v4l2out_config_alpha (handle, 255);
}
if (handle->allocated) {
handle->pending_close = TRUE;
} else {
if (handle->v4l2_fd) {
GST_DEBUG ("close V4L2 device.");
close (handle->v4l2_fd);
handle->v4l2_fd = 0;
}
g_slice_free1 (sizeof(IMXV4l2Handle), handle);
}
}
return 0;
}
static GstCaps *
gst_imx_v4l2capture_get_device_caps ()
{
#define V4L2_DEVICE_MAX 32
gint i;
char devname[20];
GstCaps *caps = NULL;
gpointer v4l2handle;
for (i=0; i<V4L2_DEVICE_MAX; i++){
sprintf(devname, "/dev/video%d", i);
v4l2handle = gst_imx_v4l2_open_device (devname, \
V4L2_BUF_TYPE_VIDEO_CAPTURE);
if (v4l2handle){
if (!caps)
caps = gst_caps_new_empty ();
if (caps) {
GstCaps *dev_caps = gst_imx_v4l2_get_caps(v4l2handle);
if (dev_caps)
gst_caps_append (caps, dev_caps);
}
gst_imx_v4l2_close_device (v4l2handle);
v4l2handle = NULL;
}
}
return caps;
}
gint gst_imx_v4l2out_get_res (gpointer v4l2handle, guint *w, guint *h)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
if (handle && handle->v4l2_fd) {
*w = handle->disp_w;
*h = handle->disp_h;
}
return 0;
}
gint gst_imx_v4l2capture_config (gpointer v4l2handle, guint fmt, guint w, guint h, guint fps_n, guint fps_d)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
return (*handle->dev_itf.v4l2capture_config) (v4l2handle, fmt, w, h, fps_n, fps_d);
}
gint gst_imx_v4l2out_config_input (gpointer v4l2handle, guint fmt, guint w, guint h, IMXV4l2Rect *crop)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
gint ret;
memcpy (&handle->in_crop, crop, sizeof(IMXV4l2Rect));
ret = (*handle->dev_itf.v4l2out_config_input) (handle, fmt, w, h, crop);
if (ret == 1) {
GST_WARNING ("Video is invisible as all input are cropped.");
handle->invisible |= INVISIBLE_IN;
return 0;
}
handle->invisible &= ~INVISIBLE_IN;
return ret;
}
static gboolean
gst_imx_v4l2out_calc_crop (IMXV4l2Handle *handle,
IMXV4l2Rect *rect_out, GstVideoRectangle *result, IMXV4l2Rect *rect_crop)
{
gdouble ratio;
gboolean need_crop = FALSE;
ratio = (gdouble)handle->in_crop.width / (gdouble)result->w;
if ((rect_out->left + result->x) < 0) {
rect_crop->left = -(rect_out->left + result->x) * ratio;
rect_crop->width = MIN (handle->in_crop.width - rect_crop->left, handle->disp_w * ratio);
rect_out->width = MIN ((result->w + (rect_out->left + result->x)), handle->disp_w);
rect_out->left = 0;
need_crop = TRUE;
}
else if ((rect_out->left + result->x + result->w) > handle->disp_w) {
rect_crop->left = 0;
rect_crop->width = (handle->disp_w - (rect_out->left + result->x)) * ratio;
if ((gint)rect_crop->width < 0)
rect_crop->width = 0;
rect_out->left = rect_out->left + result->x;
rect_out->width = handle->disp_w - rect_out->left;
need_crop = TRUE;
}
else {
rect_crop->left = 0;
rect_crop->width = handle->in_crop.width;
rect_out->left += result->x;
rect_out->width = result->w;
}
if ((rect_out->top + result->y) < 0) {
rect_crop->top = -(rect_out->top + result->y) * ratio;
rect_crop->height = MIN (handle->in_crop.height - rect_crop->top, handle->disp_h * ratio);
rect_out->height = MIN ((result->h + (rect_out->top + result->y)), handle->disp_h);
rect_out->top = 0;
need_crop = TRUE;
}
else if ((rect_out->top + result->y + result->h) > handle->disp_h) {
rect_crop->top = 0;
rect_crop->height = (handle->disp_h - (rect_out->top + result->y)) * ratio;
if ((gint)rect_crop->height < 0)
rect_crop->height = 0;
rect_out->top = rect_out->top + result->y;
rect_out->height = handle->disp_h - rect_out->top;
need_crop = TRUE;
}
else {
rect_crop->top = 0;
rect_crop->height = handle->in_crop.height;
rect_out->top += result->y;
rect_out->height = result->h;
}
if (!need_crop && handle->prev_need_crop)
need_crop = TRUE;
handle->prev_need_crop = need_crop;
return need_crop;
}
gint gst_imx_v4l2out_config_output (gpointer v4l2handle, IMXV4l2Rect *overlay, gboolean keep_video_ratio)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_crop crop;
IMXV4l2Rect *rect = &crop.c;;
GstVideoRectangle src, dest, result;
gboolean brotate;
gint ret = 0;
memcpy (rect, overlay, sizeof(IMXV4l2Rect));
GST_DEBUG ("config output, (%d, %d) -> (%d, %d)",
rect->left, rect->top, rect->width, rect->height);
if (handle->rotate != 0) {
if (rect->left < 0 || rect->top < 0
|| (rect->left + rect->width) > handle->disp_w
|| (rect->top + rect->height) > handle->disp_h) {
g_print ("not support video out of screen if oritation is not landscape.\n");
return -1;
}
}
memcpy(&handle->overlay, overlay, sizeof(IMXV4l2Rect));
brotate = handle->rotate == 90 || handle->rotate == 270;
//keep video ratio with display
src.x = src.y = 0;
src.w = handle->in_crop.width;
src.h = handle->in_crop.height;
dest.x = dest.y = 0;
if (brotate) {
dest.w = rect->height;
dest.h = rect->width;
}
else {
dest.w = rect->width;
dest.h = rect->height;
}
if (keep_video_ratio)
gst_video_sink_center_rect (src, dest, &result, TRUE);
else
memcpy (&result, &dest, sizeof(GstVideoRectangle));
GST_DEBUG ("src, (%d, %d) -> (%d, %d).", src.x, src.y, src.w, src.h);
GST_DEBUG ("dest, (%d, %d) -> (%d, %d).", dest.x, dest.y, dest.w, dest.h);
GST_DEBUG ("result, (%d, %d) -> (%d, %d).", result.x, result.y, result.w, result.h);
if (handle->rotate == 0) {
// only support video out of screen in landscape mode
IMXV4l2Rect rect_crop;
if (gst_imx_v4l2out_calc_crop (handle, rect, &result, &rect_crop)) {
ret = (*handle->dev_itf.v4l2out_config_input) (handle, handle->in_fmt, handle->in_w, handle->in_h, &rect_crop);
if (ret == 1) {
handle->invisible |= INVISIBLE_OUT;
GST_DEBUG ("Video is invisible as out of display.");
return 1;
}
else if (ret < 0)
return ret;
else
handle->invisible &= ~INVISIBLE_OUT;
ret = 2;
}
}
else {
if (brotate) {
rect->left += result.y;
rect->top += result.x;
rect->width = result.h;
rect->height = result.w;
}
else {
rect->left += result.x;
rect->top += result.y;
rect->width = result.w;
rect->height = result.h;
}
}
if (rect->left >= handle->disp_w || rect->top >= handle->disp_h) {
handle->invisible |= INVISIBLE_OUT;
GST_DEBUG ("Video is invisible as out of display.");
return 1;
}
handle->invisible &= ~INVISIBLE_OUT;
GST_DEBUG ("rect, (%d, %d) -> (%d, %d).", rect->left, rect->top, rect->width, rect->height);
if ((*handle->dev_itf.v4l2out_config_output) (handle, &crop) < 0) {
GST_ERROR ("v4l2out_config_output failed.");
return -1;
}
return ret;
}
gint gst_imx_v4l2_config_rotate (gpointer v4l2handle, gint rotate)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
GST_DEBUG ("set rotation to (%d).", rotate);
if (rotate != 0) {
if (handle->overlay.left < 0 || handle->overlay.top < 0
|| (handle->overlay.left + handle->overlay.width) > handle->disp_w
|| (handle->overlay.top + handle->overlay.height) > handle->disp_h) {
g_print ("not support video out of screen if orientation is not landscape.\n");
return -1;
}
}
if ((*handle->dev_itf.v4l2out_config_rotate) (handle, rotate) < 0) {
return -1;
}
handle->rotate = rotate;
return 0;
}
gint gst_imx_v4l2_config_flip (gpointer v4l2handle, guint flip)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
GST_DEBUG ("set flip to (%d).", flip);
if (flip != V4L2_CID_VFLIP && flip != V4L2_CID_HFLIP) {
g_print ("input flip orientation is not correct.\n");
return -1;
}
if ((*handle->dev_itf.v4l2out_config_flip) (handle, flip) < 0) {
return -1;
}
handle->flip = flip;
return 0;
}
gint gst_imx_v4l2_config_deinterlace (gpointer v4l2handle, gboolean do_deinterlace, guint motion)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_control ctrl;
handle->v4l2_hold_buf_num = V4L2_HOLDED_BUFFERS;
if (gst_imx_v4l2_support_deinterlace(V4L2_BUF_TYPE_VIDEO_OUTPUT)) {
if (do_deinterlace) {
ctrl.id = V4L2_CID_MXC_MOTION;
ctrl.value = motion;
if (ioctl(handle->v4l2_fd, VIDIOC_S_CTRL, &ctrl) < 0) {
GST_WARNING ("Set ctrl motion failed\n");
return -1;
}
if (motion < 2)
handle->v4l2_hold_buf_num = V4L2_HOLDED_BUFFERS + 2;
}
handle->do_deinterlace = do_deinterlace;
} else {
handle->do_deinterlace = FALSE;
}
return 0;
}
void gst_imx_v4l2out_config_alpha (gpointer v4l2handle, guint alpha)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
if (handle->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
GST_ERROR ("Can't set alpha for non output device.");
return;
}
(*handle->dev_itf.v4l2out_config_alpha) (handle, alpha);
return;
}
void gst_imx_v4l2out_config_color_key (gpointer v4l2handle, gboolean enable, guint color_key)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
if (handle->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
GST_ERROR ("Can't set color key for non output device.");
return;
}
(*handle->dev_itf.v4l2out_config_colorkey) (handle, enable, color_key);
}
static void * gst_imx_v4l2_find_buffer(gpointer v4l2handle, PhyMemBlock *memblk)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
gint i;
for(i=0; i<MAX_BUFFER; i++) {
if (handle->buffer_pair[i].vaddr == memblk->vaddr)
return &handle->buffer_pair[i].v4l2buffer;
}
if (handle->memory_mode == V4L2_MEMORY_USERPTR) {
struct v4l2_buffer *v4l2buf;
if (handle->allocated >= MAX_BUFFER) {
GST_ERROR ("No more v4l2 buffer for allocating.\n");
return -1;
}
v4l2buf = &handle->buffer_pair[handle->allocated].v4l2buffer;
memset (v4l2buf, 0, sizeof(struct v4l2_buffer));
v4l2buf->type = handle->type;
v4l2buf->memory = handle->memory_mode;
v4l2buf->index = handle->allocated;
v4l2buf->m.userptr = memblk->paddr;
v4l2buf->length = memblk->size;
handle->buffer_pair[handle->allocated].vaddr = memblk->vaddr;
handle->allocated ++;
GST_DEBUG ("Allocated v4l2buffer(%p), type(%d), index(%d), memblk(%p), vaddr(%p), paddr(%p), size(%d).",
v4l2buf, v4l2buf->type, handle->allocated - 1, memblk, memblk->vaddr, memblk->paddr, memblk->size);
return v4l2buf;
}
GST_ERROR ("Can't find the buffer 0x%08X.", memblk->paddr);
return NULL;
}
gint gst_imx_v4l2_set_buffer_count (gpointer v4l2handle, guint count, guint memory_mode)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_requestbuffers buf_req;
GST_DEBUG ("requeset for (%d) buffers.", count);
memset(&buf_req, 0, sizeof(buf_req));
buf_req.type = handle->type;
buf_req.count = count;
handle->memory_mode = buf_req.memory = memory_mode;
if (ioctl(handle->v4l2_fd, VIDIOC_REQBUFS, &buf_req) < 0) {
GST_ERROR("Request %d buffers failed\n", count);
return -1;
}
handle->buffer_count = count;
return 0;
}
gint gst_imx_v4l2_allocate_buffer (gpointer v4l2handle, PhyMemBlock *memblk)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer *v4l2buf;
if (handle->memory_mode == V4L2_MEMORY_USERPTR) {
GST_INFO ("USERPTR mode, needn't allocate memory.\n");
return 0;
}
if (handle->allocated >= handle->buffer_count) {
GST_ERROR ("No more v4l2 buffer for allocating.\n");
return -1;
}
v4l2buf = &handle->buffer_pair[handle->allocated].v4l2buffer;
memset (v4l2buf, 0, sizeof(struct v4l2_buffer));
v4l2buf->type = handle->type;
v4l2buf->memory = handle->memory_mode;
v4l2buf->index = handle->allocated;
if (ioctl(handle->v4l2_fd, VIDIOC_QUERYBUF, v4l2buf) < 0) {
GST_ERROR ("VIDIOC_QUERYBUF error.");
return -1;
}
GST_DEBUG ("Allocated v4l2buffer(%p), type(%d), memblk(%p), paddr(%p), size(%d).",
v4l2buf, v4l2buf->type, memblk, v4l2buf->m.offset, v4l2buf->length);
memblk->size = v4l2buf->length;
memblk->vaddr = mmap (NULL, v4l2buf->length, PROT_READ | PROT_WRITE, MAP_SHARED, handle->v4l2_fd, v4l2buf->m.offset);
if (!memblk->vaddr) {
GST_ERROR ("mmap v4lbuffer address failed\n");
return -1;
}
//FIXME: need to query twice to get the physical address
if (ioctl(handle->v4l2_fd, VIDIOC_QUERYBUF, v4l2buf) < 0) {
GST_ERROR ("VIDIOC_QUERYBUF for physical address failed.");
return -1;
}
memblk->paddr = (guchar*) v4l2buf->m.offset;
// if the queried physical address is 0, that means the m.offset is not
// a absolute physical address.
if (NULL == memblk->paddr)
handle->invalid_paddr = TRUE;
// clear all the following paddr since m.offset is not a valid address
// so that the caller can check if a memblk contain a valid physical address
// by checking the paddr.
if (handle->invalid_paddr)
memblk->paddr = NULL;
handle->buffer_pair[handle->allocated].vaddr = memblk->vaddr;
handle->allocated ++;
GST_DEBUG ("Allocated v4l2buffer(%p), type(%d), index(%d), memblk(%p), vaddr(%p), paddr(%p), size(%d).",
v4l2buf, v4l2buf->type, handle->allocated - 1, memblk, memblk->vaddr, memblk->paddr, memblk->size);
return 0;
}
gint gst_imx_v4l2_register_buffer (gpointer v4l2handle, PhyMemBlock *memblk)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer *v4l2buf;
if (handle->allocated >= handle->buffer_count) {
GST_ERROR ("No more v4l2 buffer for allocating.\n");
return -1;
}
v4l2buf = &handle->buffer_pair[handle->allocated].v4l2buffer;
memset (v4l2buf, 0, sizeof(struct v4l2_buffer));
v4l2buf->type = handle->type;
v4l2buf->memory = handle->memory_mode;
v4l2buf->index = handle->allocated;
v4l2buf->m.userptr = memblk->paddr;
v4l2buf->length = memblk->size;
handle->buffer_pair[handle->allocated].vaddr = memblk->vaddr;
if (ioctl(handle->v4l2_fd, VIDIOC_QUERYBUF, v4l2buf) < 0) {
GST_ERROR ("VIDIOC_QUERYBUF error.");
return -1;
}
handle->allocated ++;
GST_DEBUG ("Allocated v4l2buffer(%p), memblk(%p), paddr(%p), index(%d).",
v4l2buf, memblk, memblk->paddr, handle->allocated - 1);
return 0;
}
gint gst_imx_v4l2_free_buffer (gpointer v4l2handle, PhyMemBlock *memblk)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer *v4l2buf;
v4l2buf = (struct v4l2_buffer *)gst_imx_v4l2_find_buffer(v4l2handle, memblk);
if (memblk->vaddr)
munmap(memblk->vaddr, memblk->size);
if (v4l2buf) {
GST_DEBUG ("Free v4l2buffer(%p), memblk(%p), paddr(%p), index(%d).",
v4l2buf, memblk, memblk->paddr, v4l2buf->index);
handle->buffer_pair[v4l2buf->index].vaddr = 0;
memset (v4l2buf, 0, sizeof(struct v4l2_buffer));
}
handle->allocated --;
if (handle->allocated < 0) {
GST_WARNING ("freed buffer more than allocated.");
handle->allocated = 0;
}
if (handle->memory_mode == V4L2_MEMORY_USERPTR) {
handle->allocated = 0;
}
if (handle->allocated == 0 && handle->pending_close) {
handle->pending_close = FALSE;
if (handle->v4l2_fd) {
close (handle->v4l2_fd);
handle->v4l2_fd = 0;
}
g_slice_free1 (sizeof(IMXV4l2Handle), handle);
}
return 0;
}
static gint imx_v4l2_do_queue_buffer (IMXV4l2Handle *handle, struct v4l2_buffer *v4l2buf)
{
struct timeval queuetime;
if (!v4l2buf) {
GST_ERROR ("queue buffer is NULL!!");
return -1;
}
/*display immediately */
gettimeofday (&queuetime, NULL);
v4l2buf->timestamp = queuetime;
if (ioctl (handle->v4l2_fd, VIDIOC_QBUF, v4l2buf) < 0) {
GST_ERROR ("queue v4l2 buffer failed.");
return -1;
}
return 0;
}
gint gst_imx_v4l2_queue_v4l2memblk (gpointer v4l2handle, PhyMemBlock *memblk, GstVideoFrameFlags flags)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer *v4l2buf;
gint index;
v4l2buf = (struct v4l2_buffer *)gst_imx_v4l2_find_buffer(v4l2handle, memblk);
if (!v4l2buf)
return -1;
index = v4l2buf->index;
GST_DEBUG ("queue v4lbuffer memblk (%p), paddr(%p), index(%d), flags(%x).",
memblk, memblk->paddr, index, flags);
v4l2buf->field = V4L2_FIELD_NONE;
if ((flags & GST_VIDEO_FRAME_FLAG_INTERLACED) && handle->do_deinterlace) {
if (flags & GST_VIDEO_FRAME_FLAG_TFF)
v4l2buf->field = V4L2_FIELD_INTERLACED_TB;
else
v4l2buf->field = V4L2_FIELD_INTERLACED_BT;
}
if (flags & GST_VIDEO_FRAME_FLAG_ONEFIELD) {
if (flags & GST_VIDEO_FRAME_FLAG_TFF)
v4l2buf->field = V4L2_FIELD_TOP;
else
v4l2buf->field = V4L2_FIELD_BOTTOM;
}
handle->buffer_pair[v4l2buf->index].v4l2memblk = memblk;
if (!handle->streamon) {
int i;
GST_DEBUG ("streamon count (%d), queue count (%d)", handle->streamon_count, handle->queued_count);
handle->v4lbuf_queued_before_streamon[handle->queued_count] = v4l2buf;
handle->queued_count ++;
if (handle->queued_count < handle->streamon_count)
return 0;
for (i=0; i<handle->streamon_count; i++) {
if (imx_v4l2_do_queue_buffer (handle, handle->v4lbuf_queued_before_streamon[i]) < 0) {
handle->buffer_pair[handle->v4lbuf_queued_before_streamon[i]->index].v4l2memblk = NULL;
GST_ERROR ("queue buffers before streamon failed.");
return -1;
}
}
if (ioctl (handle->v4l2_fd, VIDIOC_STREAMON, &handle->type) < 0) {
GST_ERROR ("Stream on V4L2 device failed.\n");
return -1;
}
handle->streamon = TRUE;
GST_DEBUG ("V4L2 device is STREAMON.");
return 0;
}
if (imx_v4l2_do_queue_buffer (handle, v4l2buf) < 0) {
handle->buffer_pair[v4l2buf->index].v4l2memblk = NULL;
return -1;
}
handle->queued_count ++;
GST_DEBUG ("queued (%d)\n", handle->queued_count);
return 0;
}
gint gst_imx_v4l2_queue_gstbuffer (gpointer v4l2handle, GstBuffer *buffer, GstVideoFrameFlags flags)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer *v4l2buf;
PhyMemBlock *memblk;
if (handle->invisible) {
gst_buffer_unref (buffer);
return 0;
}
memblk = gst_buffer_query_phymem_block(buffer);
if (!memblk) {
GST_ERROR ("Can't get physical memory block from gstbuffer.\n");
return -1;
}
#if 0
{
FILE *fp = fopen("dump.yuv", "ab");
if(fp) {
GstMapInfo info;
gst_buffer_map (buffer, &info, GST_MAP_READ);
fwrite (info.data, 1, info.size, fp);
fclose (fp);
gst_buffer_unmap (buffer, &info);
}
}
#endif
GST_DEBUG ("queue gstbuffer(%p).", buffer);
v4l2buf = (struct v4l2_buffer *) gst_imx_v4l2_find_buffer(v4l2handle, memblk);
if (!v4l2buf)
return -1;
if (handle->buffer_pair[v4l2buf->index].gstbuffer) {
if (handle->buffer_pair[v4l2buf->index].gstbuffer != buffer) {
GST_WARNING ("new buffer (%p) use the same memblk(%p) with queued buffer(%p)",
buffer, memblk, handle->buffer_pair[v4l2buf->index].gstbuffer);
}
GST_WARNING ("gstbuffer(%p) for (%p) not dequeued yet but queued again, index(%d).",
handle->buffer_pair[v4l2buf->index].gstbuffer, index);
}
if (gst_imx_v4l2_queue_v4l2memblk (v4l2handle, memblk, flags) < 0) {
GST_ERROR ("queue gstbuffer (%p) failed.", buffer);
return 0;
}
handle->buffer_pair[v4l2buf->index].gstbuffer = buffer;
return 0;
}
#define TRY_TIMEOUT (500000) //500ms
#define TRY_INTERVAL (1000) //1ms
#define MAX_TRY_CNT (TRY_TIMEOUT/TRY_INTERVAL)
gint gst_imx_v4l2_dequeue_v4l2memblk (gpointer v4l2handle, PhyMemBlock **memblk,
GstVideoFrameFlags * flags)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
struct v4l2_buffer v4l2buf;
gint trycnt = 0;
if (handle->queued_count <= MAX(handle->v4l2_hold_buf_num, handle->streamon_count)) {
GST_DEBUG ("current queued %d", handle->queued_count);
*memblk = NULL;
return 0;
}
memset (&v4l2buf, 0, sizeof(v4l2buf));
v4l2buf.type = handle->type;
v4l2buf.memory = handle->memory_mode;
while (ioctl (handle->v4l2_fd, VIDIOC_DQBUF, &v4l2buf) < 0) {
trycnt ++;
if(trycnt >= MAX_TRY_CNT) {
GST_ERROR ("Dequeue buffer from v4l2 device failed.");
return -1;
}
usleep (TRY_INTERVAL);
}
if (v4l2buf.field == V4L2_FIELD_INTERLACED) {
if (handle->id == V4L2_STD_NTSC) {
v4l2buf.field = V4L2_FIELD_INTERLACED_BT;
} else {
v4l2buf.field = V4L2_FIELD_INTERLACED_TB;
}
}
/* set field info */
switch (v4l2buf.field) {
case V4L2_FIELD_NONE: *flags = GST_VIDEO_FRAME_FLAG_NONE; break;
case V4L2_FIELD_TOP: *flags =
GST_VIDEO_FRAME_FLAG_ONEFIELD | GST_VIDEO_FRAME_FLAG_TFF; break;
case V4L2_FIELD_BOTTOM: *flags = GST_VIDEO_FRAME_FLAG_ONEFIELD; break;
case V4L2_FIELD_INTERLACED_TB: *flags =
GST_VIDEO_FRAME_FLAG_INTERLACED | GST_VIDEO_FRAME_FLAG_TFF; break;
case V4L2_FIELD_INTERLACED_BT: *flags = GST_VIDEO_FRAME_FLAG_INTERLACED; break;
default: GST_WARNING("unknown field type"); break;
}
*memblk = handle->buffer_pair[v4l2buf.index].v4l2memblk;
GST_DEBUG ("deque v4l2buffer memblk (%p), paddr(%p), index (%d)",
*memblk, (*memblk)->paddr, v4l2buf.index);
handle->buffer_pair[v4l2buf.index].v4l2memblk = NULL;
handle->queued_count--;
GST_DEBUG ("deque v4l2buffer memblk (%p), index (%d), flags (%d)",
v4l2buf.index, handle->buffer_pair[v4l2buf.index].v4l2memblk, *flags);
return 0;
}
gint gst_imx_v4l2_dequeue_gstbuffer (gpointer v4l2handle, GstBuffer **buffer,
GstVideoFrameFlags * flags)
{
IMXV4l2Handle *handle = (IMXV4l2Handle*)v4l2handle;
PhyMemBlock *memblk = NULL;
struct v4l2_buffer *v4l2buf;
if (handle->invisible) {
return 0;
}
if (gst_imx_v4l2_dequeue_v4l2memblk (handle, &memblk, flags) < 0) {
GST_ERROR ("dequeue memblk failed.");
return -1;
}
if (!memblk)
return 0;
v4l2buf = (struct v4l2_buffer *) gst_imx_v4l2_find_buffer(v4l2handle, memblk);
if (!v4l2buf)
return -1;
*buffer = handle->buffer_pair[v4l2buf->index].gstbuffer;
handle->buffer_pair[v4l2buf->index].gstbuffer = NULL;
GST_DEBUG ("dequeue gstbuffer(%p), v4l2buffer index(%d).", *buffer, v4l2buf->index);
return 0;
}