From 135749a0e0bc337e428888edc120feaf268bbda5 Mon Sep 17 00:00:00 2001 From: Changtao Hu Date: Tue, 27 Jun 2017 13:56:42 +0800 Subject: [PATCH] gstreamer upgrade: v4l2 for good plugins Make patch for gstreamer upgrade Test: OK Change-Id: Icb4e241d2e86b2cf3b12e543a1df09ce8b264a5d Signed-off-by: Changtao Hu CR-Id: AUTO00005189 --- sys/v4l2/Makefile.am | 11 +- sys/v4l2/ext/videodev2.h | 17 + sys/v4l2/gstv4l2.c | 13 +- sys/v4l2/gstv4l2bufferpool.c | 113 ++++- sys/v4l2/gstv4l2h264enc.c | 204 +++++++++ sys/v4l2/gstv4l2h264enc.h | 63 +++ sys/v4l2/gstv4l2mtkjpegdec.c | 183 +++++++++ sys/v4l2/gstv4l2mtkjpegdec.h | 63 +++ sys/v4l2/gstv4l2mtkvpudec.c | 181 ++++++++ sys/v4l2/gstv4l2mtkvpudec.h | 63 +++ sys/v4l2/gstv4l2object.c | 564 ++++++++++++++++++++++++- sys/v4l2/gstv4l2object.h | 5 + sys/v4l2/gstv4l2src.c | 2 +- sys/v4l2/gstv4l2videodec.c | 225 +++++++++- sys/v4l2/gstv4l2videoenc.c | 958 +++++++++++++++++++++++++++++++++++++++++++ sys/v4l2/gstv4l2videoenc.h | 103 +++++ sys/v4l2/v4l2_calls.c | 31 +- 17 files changed, 2780 insertions(+), 19 deletions(-) mode change 100644 => 100755 sys/v4l2/Makefile.am mode change 100644 => 100755 sys/v4l2/ext/videodev2.h mode change 100644 => 100755 sys/v4l2/gstv4l2.c mode change 100644 => 100755 sys/v4l2/gstv4l2bufferpool.c create mode 100755 sys/v4l2/gstv4l2h264enc.c create mode 100755 sys/v4l2/gstv4l2h264enc.h create mode 100755 sys/v4l2/gstv4l2mtkjpegdec.c create mode 100755 sys/v4l2/gstv4l2mtkjpegdec.h create mode 100755 sys/v4l2/gstv4l2mtkvpudec.c create mode 100755 sys/v4l2/gstv4l2mtkvpudec.h mode change 100644 => 100755 sys/v4l2/gstv4l2object.c mode change 100644 => 100755 sys/v4l2/gstv4l2object.h mode change 100644 => 100755 sys/v4l2/gstv4l2src.c mode change 100644 => 100755 sys/v4l2/gstv4l2videodec.c create mode 100755 sys/v4l2/gstv4l2videoenc.c create mode 100755 sys/v4l2/gstv4l2videoenc.h mode change 100644 => 100755 sys/v4l2/v4l2_calls.c diff --git a/sys/v4l2/Makefile.am b/sys/v4l2/Makefile.am old mode 100644 new mode 100755 index 5ccea1a..eb48423 --- a/sys/v4l2/Makefile.am +++ b/sys/v4l2/Makefile.am @@ -14,6 +14,10 @@ libgstvideo4linux2_la_SOURCES = gstv4l2.c \ gstv4l2tuner.c \ gstv4l2transform.c \ gstv4l2videodec.c \ + gstv4l2mtkvpudec.c \ + gstv4l2mtkjpegdec.c \ + gstv4l2videoenc.c \ + gstv4l2h264enc.c \ gstv4l2vidorient.c \ v4l2_calls.c \ v4l2-utils.c \ @@ -37,7 +41,8 @@ libgstvideo4linux2_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ $(GST_LIBS) \ $(LIBV4L2_LIBS) \ $(GUDEV_LIBS) \ - $(LIBRT) + $(LIBRT) \ + -lmtkconv noinst_HEADERS = \ ext/types-compat.h \ @@ -55,6 +60,10 @@ noinst_HEADERS = \ gstv4l2tuner.h \ gstv4l2transform.h \ gstv4l2videodec.h \ + gstv4l2mtkvpudec.h \ + gstv4l2mtkjpegdec.h \ + gstv4l2videoenc.h \ + gstv4l2h264enc.h \ gstv4l2vidorient.h \ v4l2_calls.h \ v4l2-utils.h \ diff --git a/sys/v4l2/ext/videodev2.h b/sys/v4l2/ext/videodev2.h old mode 100644 new mode 100755 index 68e82be..9a9bcd0 --- a/sys/v4l2/ext/videodev2.h +++ b/sys/v4l2/ext/videodev2.h @@ -525,6 +525,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_NV61M v4l2_fourcc('N', 'M', '6', '1') /* 16 Y/CrCb 4:2:2 */ #define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 macroblocks */ #define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 macroblocks */ +#define V4L2_PIX_FMT_MT21 v4l2_fourcc('M', 'T', '2', '1') /* three non contiguous planes - Y, Cb, Cr */ #define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12 YUV420 planar */ @@ -565,17 +566,27 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_JPEG v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG */ #define V4L2_PIX_FMT_DV v4l2_fourcc('d', 'v', 's', 'd') /* 1394 */ #define V4L2_PIX_FMT_MPEG v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 Multiplexed */ +#define V4L2_PIX_FMT_H265 v4l2_fourcc('H', '2', '6', '5') /* H265 with start codes */ #define V4L2_PIX_FMT_H264 v4l2_fourcc('H', '2', '6', '4') /* H264 with start codes */ #define V4L2_PIX_FMT_H264_NO_SC v4l2_fourcc('A', 'V', 'C', '1') /* H264 without start codes */ #define V4L2_PIX_FMT_H264_MVC v4l2_fourcc('M', '2', '6', '4') /* H264 MVC */ #define V4L2_PIX_FMT_H263 v4l2_fourcc('H', '2', '6', '3') /* H263 */ +#define V4L2_PIX_FMT_S263 v4l2_fourcc('S', '2', '6', '3') /* S263 */ #define V4L2_PIX_FMT_MPEG1 v4l2_fourcc('M', 'P', 'G', '1') /* MPEG-1 ES */ #define V4L2_PIX_FMT_MPEG2 v4l2_fourcc('M', 'P', 'G', '2') /* MPEG-2 ES */ #define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG-4 part 2 ES */ +#define V4L2_PIX_FMT_RV30 v4l2_fourcc('R', 'V', '3', '0') /* RV30 ES */ +#define V4L2_PIX_FMT_RV40 v4l2_fourcc('R', 'V', '4', '0') /* RV40 ES */ #define V4L2_PIX_FMT_XVID v4l2_fourcc('X', 'V', 'I', 'D') /* Xvid */ +#define V4L2_PIX_FMT_DIVX v4l2_fourcc('D', 'I', 'V', 'X') /* Divx */ +#define V4L2_PIX_FMT_DIVX3 v4l2_fourcc('D', 'I', 'V', '3') /* Divx3 */ +#define V4L2_PIX_FMT_DIVX4 v4l2_fourcc('D', 'I', 'V', '4') /* Divx4 */ +#define V4L2_PIX_FMT_DIVX5 v4l2_fourcc('D', 'I', 'V', '5') /* Divx5 */ +#define V4L2_PIX_FMT_DIVX6 v4l2_fourcc('D', 'I', 'V', '6') /* Divx6 */ #define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */ #define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */ #define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0') /* VP8 */ +#define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0') /* VP9 */ /* Vendor-specific formats */ #define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ @@ -605,6 +616,12 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SE401 v4l2_fourcc('S', '4', '0', '1') /* se401 janggu compressed rgb */ #define V4L2_PIX_FMT_S5C_UYVY_JPG v4l2_fourcc('S', '5', 'C', 'I') /* S5C73M3 interleaved UYVY/JPEG */ +#define V4L2_PIX_FMT_WMV1 v4l2_fourcc('W', 'M', 'V', '1') /* WMV7 */ +#define V4L2_PIX_FMT_WMV2 v4l2_fourcc('W', 'M', 'V', '2') /* WMV8 */ +#define V4L2_PIX_FMT_WMV3 v4l2_fourcc('W', 'M', 'V', '3') /* WMV9 */ +#define V4L2_PIX_FMT_WMVA v4l2_fourcc('W', 'M', 'V', 'A') /* WMVA */ +#define V4L2_PIX_FMT_WVC1 v4l2_fourcc('W', 'V', 'C', '1') /* VC1 */ + /* SDR formats - used only for Software Defined Radio devices */ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ diff --git a/sys/v4l2/gstv4l2.c b/sys/v4l2/gstv4l2.c old mode 100644 new mode 100755 index 83fcc98..1375c11 --- a/sys/v4l2/gstv4l2.c +++ b/sys/v4l2/gstv4l2.c @@ -49,6 +49,9 @@ #include "gstv4l2videodec.h" #include "gstv4l2deviceprovider.h" #include "gstv4l2transform.h" +#include "gstv4l2h264enc.h" +#include "gstv4l2mtkvpudec.h" +#include "gstv4l2mtkjpegdec.h" /* used in v4l2_calls.c and v4l2src_calls.c */ GST_DEBUG_CATEGORY (v4l2_debug); @@ -177,8 +180,14 @@ gst_v4l2_probe_and_register (GstPlugin * plugin) basename = g_path_get_basename (it->device_path); - if (gst_v4l2_is_video_dec (sink_caps, src_caps)) - ret = gst_v4l2_video_dec_register (plugin, basename, it->device_path, + if (gst_v4l2_is_mtk_jpeg_dec (sink_caps, src_caps)) + ret = gst_v4l2_mtk_jpeg_dec_register (plugin, basename, it->device_path, + sink_caps, src_caps); + else if (gst_v4l2_is_mtk_vpu_dec (sink_caps, src_caps)) + ret = gst_v4l2_mtk_vpu_dec_register (plugin, basename, it->device_path, + sink_caps, src_caps); + else if (gst_v4l2_is_h264_enc (sink_caps, src_caps)) + ret = gst_v4l2_h264_enc_register (plugin, basename, it->device_path, sink_caps, src_caps); else if (gst_v4l2_is_transform (sink_caps, src_caps)) ret = gst_v4l2_transform_register (plugin, basename, it->device_path, diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c old mode 100644 new mode 100755 index 3184bd8..86ed7e9 --- a/sys/v4l2/gstv4l2bufferpool.c +++ b/sys/v4l2/gstv4l2bufferpool.c @@ -46,6 +46,8 @@ #include "gst/gst-i18n-plugin.h" #include +#include "libmtkconv.h" + GST_DEBUG_CATEGORY_STATIC (v4l2bufferpool_debug); GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE); #define GST_CAT_DEFAULT v4l2bufferpool_debug @@ -69,6 +71,79 @@ enum _GstV4l2BufferPoolAcquireFlags static void gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer); +static guint checksum(guint8 *data, guint size) +{ + guint i = 0, sum = 0; + + for (i = 0; i < size; i++) + sum += data[i]; + return sum; +} + +static GstFlowReturn +gst_v4l2_buffer_tran_buffer(GstBuffer * dst, GstBuffer * src, GstVideoInfo * info) +{ + GstMapInfo inmap[2], outmap; + GstFlowReturn ret = GST_FLOW_ERROR; + guint i, j, n_mem; + GstMemory *mem; + GstVideoMeta *meta; + + //GST_ERROR("dst n_mem: %d", gst_buffer_n_memory(dst)); + + if (gst_buffer_get_size(dst) < info->size) { + goto end; + } + + n_mem = gst_buffer_n_memory(src); + if (n_mem != 2) + goto end; + + for (i = 0; i < n_mem; i++) { + mem = gst_buffer_peek_memory (src, i); + if(!gst_memory_map (mem, &inmap[i], GST_MAP_READ)) + goto src_map_err; + } + + if (!gst_buffer_map (dst, &outmap, GST_MAP_WRITE)) { + goto dst_map_err; + } + + switch (GST_VIDEO_INFO_FORMAT(info)) { + case GST_VIDEO_FORMAT_RGB16: + if (do_b_to_rgb16(outmap.data, inmap[0].data, inmap[1].data, info->width, info->height)) + goto process_err; + break; + case GST_VIDEO_FORMAT_NV12: + case GST_VIDEO_FORMAT_MT21: + if (do_b_to_nv12(outmap.data, outmap.data + info->offset[1], inmap[0].data, inmap[1].data, info->width, info->height)) + goto process_err; + break; + case GST_VIDEO_FORMAT_I420: + if (do_b_to_i420(outmap.data, outmap.data + info->offset[1], outmap.data + info->offset[2], inmap[0].data, inmap[1].data, info->width, info->height)) + goto process_err; + break; + case GST_VIDEO_FORMAT_YUY2: + if (do_b_to_yuy2(outmap.data, inmap[0].data, inmap[1].data, info->width, info->height)) + goto process_err; + break; + default: + goto process_err; + break; + } + + ret = GST_FLOW_OK; +process_err: + gst_buffer_unmap (dst, &outmap); +src_map_err: +dst_map_err: + for (j = 0; j < i; j++) + gst_memory_unmap (inmap[j].memory, &inmap[j]); +end: + return ret; +} + + static gboolean gst_v4l2_is_buffer_valid (GstBuffer * buffer, GstV4l2MemoryGroup ** out_group) { @@ -1722,6 +1797,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) case GST_V4L2_IO_DMABUF: { GstBuffer *tmp; + struct v4l2_pix_format *pix_fmt = &(obj->format.fmt.pix); if ((*buf)->pool == bpool) { guint num_queued; @@ -1756,7 +1832,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) } /* start copying buffers when we are running low on buffers */ - if (num_queued < pool->copy_threshold) { + if (num_queued < pool->copy_threshold || pix_fmt->pixelformat == V4L2_PIX_FMT_MT21) { GstBuffer *copy; if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, MMAP)) { @@ -1765,11 +1841,27 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) goto done; } - /* copy the buffer */ +#ifdef GST_V4L2_DECODER_OUTPUT_DEBUG + if (pix_fmt->pixelformat == V4L2_PIX_FMT_MT21) + { + copy = gst_buffer_new_allocate (NULL, obj->info.size, NULL); + ret = gst_v4l2_buffer_tran_buffer(copy, *buf, &obj->info); + if (ret != GST_FLOW_OK) + gst_buffer_replace (©, NULL); + } + else + { + /* copy the buffer */ + copy = gst_buffer_copy_region (*buf, + GST_BUFFER_COPY_ALL | GST_BUFFER_COPY_DEEP, 0, -1); + GST_LOG_OBJECT (pool, "copy buffer %p->%p", *buf, copy); + } + +#else copy = gst_buffer_copy_region (*buf, - GST_BUFFER_COPY_ALL | GST_BUFFER_COPY_DEEP, 0, -1); + GST_BUFFER_COPY_ALL | GST_BUFFER_COPY_DEEP, 0, -1); GST_LOG_OBJECT (pool, "copy buffer %p->%p", *buf, copy); - +#endif /* and requeue so that we can continue capturing */ gst_buffer_unref (*buf); *buf = copy; @@ -1796,8 +1888,19 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) else goto eos; } - + +#ifdef GST_V4L2_DECODER_OUTPUT_DEBUG + if (pix_fmt->pixelformat == V4L2_PIX_FMT_MT21) + { + ret = gst_v4l2_buffer_tran_buffer(*buf, tmp, &obj->info); + } + else + ret = gst_v4l2_buffer_pool_copy_buffer (pool, *buf, tmp); +#else ret = gst_v4l2_buffer_pool_copy_buffer (pool, *buf, tmp); +#endif + + GST_BUFFER_TIMESTAMP(*buf) = GST_BUFFER_TIMESTAMP(tmp); /* an queue the buffer again after the copy */ gst_v4l2_buffer_pool_release_buffer (bpool, tmp); diff --git a/sys/v4l2/gstv4l2h264enc.c b/sys/v4l2/gstv4l2h264enc.c new file mode 100755 index 0000000..bd7d34d --- /dev/null +++ b/sys/v4l2/gstv4l2h264enc.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2014 ayaka + * Copyright (C) 2016 Rick Chang + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "gstv4l2h264enc.h" +#include "v4l2_calls.h" + +#include +#include + +GST_DEBUG_CATEGORY_STATIC (gst_v4l2_h264_enc_debug); +#define GST_CAT_DEFAULT gst_v4l2_h264_enc_debug + +typedef struct +{ + gchar *device; + GstCaps *sink_caps; + GstCaps *src_caps; +} GstV4l2VideoEncCData; + +enum +{ + PROP_0, + V4L2_STD_OBJECT_PROPS, +}; + +static GstStaticPadTemplate src_template = +GST_STATIC_PAD_TEMPLATE("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS( + "video/x-h264, " + "stream-format = (string) byte-stream, " + "alignment = (string) { au }; " + ) +); + +#define gst_v4l2_h264_enc_parent_class parent_class +G_DEFINE_TYPE (GstV4l2H264Enc, gst_v4l2_h264_enc, GST_TYPE_V4L2_VIDEO_ENC); + +static GstFlowReturn +gst_v4l2_h264_enc_handle_frame (GstVideoEncoder * encoder, + GstVideoCodecFrame * frame) +{ + GstV4l2VideoEnc *parent = GST_V4L2_VIDEO_ENC (encoder); + GstStructure *structure; + GstCaps *outcaps; + + if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (parent->v4l2capture))) { + outcaps = gst_caps_new_empty_simple ("video/x-h264"); + structure = gst_caps_get_structure (outcaps, 0); + gst_structure_set (structure, "stream-format", + G_TYPE_STRING, "byte-stream", NULL); + gst_structure_set (structure, "alignment", G_TYPE_STRING, "au", NULL); + return GST_V4L2_VIDEO_ENC_CLASS (parent_class)->handle_frame + (encoder, frame, outcaps); + } + + return GST_V4L2_VIDEO_ENC_CLASS (parent_class)->handle_frame + (encoder, frame, NULL); +} + +static void +gst_v4l2_h264_enc_init (GstV4l2H264Enc * self) +{ + +} + +static void +gst_v4l2_h264_enc_class_init (GstV4l2H264EncClass * klass) +{ + GstElementClass *element_class; + GObjectClass *gobject_class; + GstV4l2VideoEncClass *v4l2_encoder_class; + GstVideoEncoderClass *baseclass; + + parent_class = g_type_class_peek_parent (klass); + + element_class = (GstElementClass *) klass; + gobject_class = (GObjectClass *) klass; + v4l2_encoder_class = GST_V4L2_VIDEO_ENC_CLASS (klass); + baseclass = GST_VIDEO_ENCODER_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_v4l2_h264_enc_debug, "v4l2mtkh264enc", 0, + "V4L2 Mtk H.264 HW Encoder"); + + gst_element_class_set_static_metadata (element_class, + "V4L2 Mtk H.264 HW Encoder", + "Codec/Encoder/Video", + "MTK H.264 HW encode via V4L2 API", + "ayaka \n" + "Rick Chang "); + + /* FIXME propose_allocation or not ? */ + baseclass->handle_frame = GST_DEBUG_FUNCPTR (gst_v4l2_h264_enc_handle_frame); +} + +/* Probing functions */ +gboolean +gst_v4l2_is_h264_enc (GstCaps * sink_caps, GstCaps * src_caps) +{ + gboolean ret = FALSE; + + if (gst_caps_is_subset (sink_caps, gst_v4l2_object_get_raw_caps ()) + && gst_caps_can_intersect (src_caps, + gst_caps_from_string ("video/x-h264"))) + ret = TRUE; + + return ret; +} + +static void +gst_v4l2_h264_enc_subinstance_init (GTypeInstance * instance, gpointer g_class) +{ + GstV4l2VideoEncClass *klass = GST_V4L2_VIDEO_ENC_CLASS (g_class); + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (instance); + + g_free (self->v4l2output->videodev); + self->v4l2output->videodev = g_strdup (klass->default_device); + + g_free (self->v4l2capture->videodev); + self->v4l2capture->videodev = g_strdup (klass->default_device); +} + +static void +gst_v4l2_h264_enc_subclass_init (gpointer g_class, gpointer data) +{ + GstV4l2VideoEncClass *klass = GST_V4L2_VIDEO_ENC_CLASS (g_class); + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstV4l2VideoEncCData *cdata = data; + + klass->default_device = cdata->device; + + /* Note: gst_pad_template_new() take the floating ref from the caps */ + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + cdata->sink_caps)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get(&src_template)); + + g_free (cdata); +} + +gboolean +gst_v4l2_h264_enc_register (GstPlugin * plugin, const gchar * basename, + const gchar * device_path, GstCaps * sink_caps, GstCaps * src_caps) +{ + GTypeQuery type_query; + GTypeInfo type_info = { 0, }; + GType type, subtype; + gchar *type_name; + GstV4l2VideoEncCData *cdata; + + cdata = g_new0 (GstV4l2VideoEncCData, 1); + cdata->device = g_strdup (device_path); + cdata->sink_caps = gst_caps_ref (sink_caps); + cdata->src_caps = gst_caps_ref (src_caps); + + type = gst_v4l2_h264_enc_get_type(); + g_type_query (type, &type_query); + memset (&type_info, 0, sizeof (type_info)); + type_info.class_size = type_query.class_size; + type_info.instance_size = type_query.instance_size; + type_info.class_data = cdata; + + type_info.class_init = gst_v4l2_h264_enc_subclass_init; + type_info.instance_init = gst_v4l2_h264_enc_subinstance_init; + + type_name = g_strdup_printf ("v4l2mtkh264enc"); + subtype = g_type_register_static (type, type_name, &type_info, 0); + + gst_element_register (plugin, type_name, GST_RANK_PRIMARY + 1, subtype); + + g_free (type_name); + + return TRUE; +} diff --git a/sys/v4l2/gstv4l2h264enc.h b/sys/v4l2/gstv4l2h264enc.h new file mode 100755 index 0000000..f0f6cbb --- /dev/null +++ b/sys/v4l2/gstv4l2h264enc.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 SUMOMO Computer Association. + * Author: ayaka + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GST_V4L2_H264_ENC_H__ +#define __GST_V4L2_H264_ENC_H__ + +#include +#include "gstv4l2videoenc.h" + +GST_DEBUG_CATEGORY_EXTERN (v4l2h264enc_debug); + +G_BEGIN_DECLS +#define GST_TYPE_V4L2_H264_ENC \ + (gst_v4l2_h264_enc_get_type()) +#define GST_V4L2_H264_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_H264_ENC,GstV4l2H264Enc)) +#define GST_V4L2_H264_ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_H264_ENC,GstV4l2H264EncClass)) +#define GST_IS_V4L2_H264_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_H264_ENC)) +#define GST_IS_V4L2_H264_ENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_H264_ENC)) +typedef struct _GstV4l2H264Enc GstV4l2H264Enc; +typedef struct _GstV4l2H264EncClass GstV4l2H264EncClass; + +struct _GstV4l2H264Enc +{ + GstV4l2VideoEnc parent; +}; + +struct _GstV4l2H264EncClass +{ + GstV4l2VideoEncClass parent_class; +}; + +GType gst_v4l2_h264_enc_get_type (void); + +gboolean gst_v4l2_is_h264_enc (GstCaps * sink_caps, GstCaps * src_caps); + +gboolean gst_v4l2_h264_enc_register (GstPlugin * plugin, + const gchar * basename, + const gchar * device_path, GstCaps * sink_caps, GstCaps * src_caps); + +G_END_DECLS +#endif /* __GST_V4L2_H264_ENC_H__ */ diff --git a/sys/v4l2/gstv4l2mtkjpegdec.c b/sys/v4l2/gstv4l2mtkjpegdec.c new file mode 100755 index 0000000..6b785bf --- /dev/null +++ b/sys/v4l2/gstv4l2mtkjpegdec.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2016 MediaTek Inc + * Author: Rick Chang + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "gstv4l2mtkjpegdec.h" +#include "v4l2_calls.h" + +#include +#include + +GST_DEBUG_CATEGORY_STATIC (gst_v4l2_mtk_jpeg_dec_debug); +#define GST_CAT_DEFAULT gst_v4l2_mtk_jpeg_dec_debug + +typedef struct +{ + gchar *device; + GstCaps *sink_caps; + GstCaps *src_caps; +} GstV4l2MtkJpegDecCData; + +enum +{ + PROP_0, + V4L2_STD_OBJECT_PROPS, +}; + +static GstStaticPadTemplate gst_mtk_jpeg_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ " + "I420, Y42B } ")) + ); + +#define gst_v4l2_mtk_jpeg_dec_parent_class parent_class +G_DEFINE_TYPE (GstV4l2MtkJpegDec, gst_v4l2_mtk_jpeg_dec, GST_TYPE_V4L2_VIDEO_DEC); + +static void +gst_v4l2_mtk_jpeg_dec_init (GstV4l2MtkJpegDec * self) +{ +} + +static void +gst_v4l2_mtk_jpeg_dec_subinstance_init (GTypeInstance * instance, gpointer g_class) +{ + GstV4l2VideoDecClass *klass = GST_V4L2_VIDEO_DEC_CLASS (g_class); + GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (instance); + GstVideoDecoder *decoder = GST_VIDEO_DECODER (instance); + + gst_video_decoder_set_packetized (decoder, TRUE); + + self->v4l2output = gst_v4l2_object_new (GST_ELEMENT (self), + V4L2_BUF_TYPE_VIDEO_OUTPUT, klass->default_device, + gst_v4l2_get_output, gst_v4l2_set_output, NULL); + self->v4l2output->no_initial_format = TRUE; + self->v4l2output->keep_aspect = FALSE; + + self->v4l2capture = gst_v4l2_object_new (GST_ELEMENT (self), + V4L2_BUF_TYPE_VIDEO_CAPTURE, klass->default_device, + gst_v4l2_get_input, gst_v4l2_set_input, NULL); + self->v4l2capture->no_initial_format = TRUE; + self->v4l2output->keep_aspect = FALSE; +} + +static void +gst_v4l2_mtk_jpeg_dec_class_init (GstV4l2MtkJpegDecClass * klass) +{ + GstElementClass *element_class; + GObjectClass *gobject_class; + GstV4l2VideoDecClass *v4l2_decoder_class; + GstVideoEncoderClass *baseclass; + + parent_class = g_type_class_peek_parent (klass); + + element_class = (GstElementClass *) klass; + gobject_class = (GObjectClass *) klass; + v4l2_decoder_class = GST_V4L2_VIDEO_DEC_CLASS (klass); + baseclass = GST_VIDEO_DECODER_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_v4l2_mtk_jpeg_dec_debug, "v4l2mtkjpegdec", 0, + "V4L2 Mtk Jpeg HW Decoder"); + + gst_element_class_set_static_metadata (element_class, + "V4L2 Mtk Jpeg HW Decoder", + "Codec/Decoder/Image", + "MTK jpeg HW decode via V4L2 API", + "Rick Chang "); +} + +static void +gst_v4l2_mtk_jpeg_dec_subclass_init (gpointer g_class, gpointer data) +{ + GstV4l2VideoDecClass *klass = GST_V4L2_VIDEO_DEC_CLASS (g_class); + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstV4l2MtkJpegDecCData *cdata = data; + + klass->default_device = cdata->device; + + /* Note: gst_pad_template_new() take the floating ref from the caps */ + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + cdata->sink_caps)); + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + cdata->src_caps)); + + g_free (cdata); +} + +gboolean +gst_v4l2_is_mtk_jpeg_dec (GstCaps * sink_caps, GstCaps * src_caps) +{ + gboolean ret = FALSE; + GstCaps *caps = gst_caps_new_empty_simple ("image/jpeg"); + + if (gst_caps_is_subset (sink_caps, caps) + && gst_caps_is_subset (src_caps, gst_v4l2_object_get_raw_caps ())) + ret = TRUE; + + gst_caps_ref (caps); + return ret; +} + +gboolean +gst_v4l2_mtk_jpeg_dec_register (GstPlugin * plugin, const gchar * basename, + const gchar * device_path, GstCaps * sink_caps, GstCaps * src_caps) +{ + GTypeQuery type_query; + GTypeInfo type_info = { 0, }; + GType type, subtype; + gchar *type_name; + GstV4l2MtkJpegDecCData *cdata; + + cdata = g_new0 (GstV4l2MtkJpegDecCData, 1); + cdata->device = g_strdup (device_path); + cdata->sink_caps = gst_caps_ref (sink_caps); + cdata->src_caps = gst_static_pad_template_get_caps(&gst_mtk_jpeg_src_template); + + type = gst_v4l2_mtk_jpeg_dec_get_type (); + g_type_query (type, &type_query); + memset (&type_info, 0, sizeof (type_info)); + type_info.class_size = type_query.class_size; + type_info.instance_size = type_query.instance_size; + type_info.class_init = gst_v4l2_mtk_jpeg_dec_subclass_init; + type_info.class_data = cdata; + type_info.instance_init = gst_v4l2_mtk_jpeg_dec_subinstance_init; + + type_name = g_strdup_printf ("v4l2mtkjpegdec"); + subtype = g_type_register_static (type, type_name, &type_info, 0); + + gst_element_register (plugin, type_name, GST_RANK_PRIMARY + 1, subtype); + + g_free (type_name); + + return TRUE; +} diff --git a/sys/v4l2/gstv4l2mtkjpegdec.h b/sys/v4l2/gstv4l2mtkjpegdec.h new file mode 100755 index 0000000..365b236 --- /dev/null +++ b/sys/v4l2/gstv4l2mtkjpegdec.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 MediaTek Inc + * Author: Rick Chang + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GST_V4L2_MTK_JPEG_DEC_H__ +#define __GST_V4L2_MTK_JPEG_DEC_H__ + +#include +#include "gstv4l2videodec.h" + +GST_DEBUG_CATEGORY_EXTERN (v4l2mtkjpegdec_debug); + +G_BEGIN_DECLS +#define GST_TYPE_V4L2_MTK_JPEG_DEC \ + (gst_v4l2_mtk_jpeg_dec_get_type()) +#define GST_V4L2_MTK_JPEG_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_MTK_JPEG_DEC,GstV4l2MtkJpegDec)) +#define GST_V4L2_MTK_JPEG_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_MTK_JPEG_DEC,GstV4l2MtkJpegDecClass)) +#define GST_IS_V4L2_MTK_JPEG_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_MTK_JPEG_DEC)) +#define GST_IS_V4L2_MTK_JPEG_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_MTK_JPEG_DEC)) +typedef struct _GstV4l2MtkJpegDec GstV4l2MtkJpegDec; +typedef struct _GstV4l2MtkJpegDecClass GstV4l2MtkJpegDecClass; + +struct _GstV4l2MtkJpegDec +{ + GstV4l2VideoDec parent; +}; + +struct _GstV4l2MtkJpegDecClass +{ + GstV4l2VideoDecClass parent_class; +}; + +GType gst_v4l2_mtk_jpeg_dec_get_type (void); + +gboolean gst_v4l2_is_mtk_jpeg_dec (GstCaps * sink_caps, GstCaps * src_caps); + +gboolean gst_v4l2_mtk_jpeg_dec_register (GstPlugin * plugin, + const gchar * basename, + const gchar * device_path, GstCaps * sink_caps, GstCaps * src_caps); + +G_END_DECLS +#endif /* __GST_V4L2_MTK_JPEG_DEC_H__ */ diff --git a/sys/v4l2/gstv4l2mtkvpudec.c b/sys/v4l2/gstv4l2mtkvpudec.c new file mode 100755 index 0000000..fd09362 --- /dev/null +++ b/sys/v4l2/gstv4l2mtkvpudec.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2016 MediaTek Inc + * Author: Rick Chang + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "gstv4l2mtkvpudec.h" +#include "v4l2_calls.h" + +#include +#include + +GST_DEBUG_CATEGORY_STATIC (gst_v4l2_mtk_vpu_dec_debug); +#define GST_CAT_DEFAULT gst_v4l2_mtk_vpu_dec_debug + +typedef struct +{ + gchar *device; + GstCaps *sink_caps; + GstCaps *src_caps; +} GstV4l2MtkVpuDecCData; + +enum +{ + PROP_0, + V4L2_STD_OBJECT_PROPS, +}; + +static GstStaticPadTemplate gst_mtk_vpu_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ " + "MT21 } ")) + ); + +#define gst_v4l2_mtk_vpu_dec_parent_class parent_class +G_DEFINE_TYPE (GstV4l2MtkVpuDec, gst_v4l2_mtk_vpu_dec, GST_TYPE_V4L2_VIDEO_DEC); + +static void +gst_v4l2_mtk_vpu_dec_init (GstV4l2MtkVpuDec * self) +{ +} + +static void +gst_v4l2_mtk_vpu_dec_subinstance_init (GTypeInstance * instance, gpointer g_class) +{ + GstV4l2VideoDecClass *klass = GST_V4L2_VIDEO_DEC_CLASS (g_class); + GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (instance); + GstVideoDecoder *decoder = GST_VIDEO_DECODER (instance); + + gst_video_decoder_set_packetized (decoder, TRUE); + + self->v4l2output = gst_v4l2_object_new (GST_ELEMENT (self), + V4L2_BUF_TYPE_VIDEO_OUTPUT, klass->default_device, + gst_v4l2_get_output, gst_v4l2_set_output, NULL); + self->v4l2output->no_initial_format = TRUE; + self->v4l2output->keep_aspect = FALSE; + + self->v4l2capture = gst_v4l2_object_new (GST_ELEMENT (self), + V4L2_BUF_TYPE_VIDEO_CAPTURE, klass->default_device, + gst_v4l2_get_input, gst_v4l2_set_input, NULL); + self->v4l2capture->no_initial_format = TRUE; + self->v4l2output->keep_aspect = FALSE; +} + +static void +gst_v4l2_mtk_vpu_dec_class_init (GstV4l2MtkVpuDecClass * klass) +{ + GstElementClass *element_class; + GObjectClass *gobject_class; + GstV4l2VideoDecClass *v4l2_decoder_class; + GstVideoEncoderClass *baseclass; + + parent_class = g_type_class_peek_parent (klass); + + element_class = (GstElementClass *) klass; + gobject_class = (GObjectClass *) klass; + v4l2_decoder_class = GST_V4L2_VIDEO_DEC_CLASS (klass); + baseclass = GST_VIDEO_DECODER_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_v4l2_mtk_vpu_dec_debug, "v4l2mtkvpudec", 0, + "V4L2 Mtk Vpu HW Decoder"); + + gst_element_class_set_static_metadata (element_class, + "V4L2 Mtk Vpu HW Decoder", + "Codec/Decoder/Video", + "MTK video HW decode via V4L2 API", + "Rick Chang "); +} + +static void +gst_v4l2_mtk_vpu_dec_subclass_init (gpointer g_class, gpointer data) +{ + GstV4l2VideoDecClass *klass = GST_V4L2_VIDEO_DEC_CLASS (g_class); + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstV4l2MtkVpuDecCData *cdata = data; + + klass->default_device = cdata->device; + + /* Note: gst_pad_template_new() take the floating ref from the caps */ + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + cdata->sink_caps)); + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + cdata->src_caps)); + + g_free (cdata); +} + +gboolean +gst_v4l2_is_mtk_vpu_dec (GstCaps * sink_caps, GstCaps * src_caps) +{ + gboolean ret = FALSE; + + if (gst_caps_is_subset (sink_caps, gst_v4l2_object_get_codec_caps ()) + && gst_caps_is_subset (src_caps, gst_v4l2_object_get_raw_caps ())) + ret = TRUE; + + return ret; +} + +gboolean +gst_v4l2_mtk_vpu_dec_register (GstPlugin * plugin, const gchar * basename, + const gchar * device_path, GstCaps * sink_caps, GstCaps * src_caps) +{ + GTypeQuery type_query; + GTypeInfo type_info = { 0, }; + GType type, subtype; + gchar *type_name; + GstV4l2MtkVpuDecCData *cdata; + + cdata = g_new0 (GstV4l2MtkVpuDecCData, 1); + cdata->device = g_strdup (device_path); + cdata->sink_caps = gst_caps_ref (sink_caps); + cdata->src_caps = gst_static_pad_template_get_caps(&gst_mtk_vpu_src_template); + + type = gst_v4l2_mtk_vpu_dec_get_type (); + g_type_query (type, &type_query); + memset (&type_info, 0, sizeof (type_info)); + type_info.class_size = type_query.class_size; + type_info.instance_size = type_query.instance_size; + type_info.class_init = gst_v4l2_mtk_vpu_dec_subclass_init; + type_info.class_data = cdata; + type_info.instance_init = gst_v4l2_mtk_vpu_dec_subinstance_init; + + type_name = g_strdup_printf ("v4l2mtkvpudec"); + subtype = g_type_register_static (type, type_name, &type_info, 0); + + gst_element_register (plugin, type_name, GST_RANK_PRIMARY + 1, subtype); + + g_free (type_name); + + return TRUE; +} diff --git a/sys/v4l2/gstv4l2mtkvpudec.h b/sys/v4l2/gstv4l2mtkvpudec.h new file mode 100755 index 0000000..3ba72fc --- /dev/null +++ b/sys/v4l2/gstv4l2mtkvpudec.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 MediaTek Inc + * Author: Rick Chang + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GST_V4L2_MTK_VPU_DEC_H__ +#define __GST_V4L2_MTK_VPU_DEC_H__ + +#include +#include "gstv4l2videodec.h" + +GST_DEBUG_CATEGORY_EXTERN (v4l2mtkvpudec_debug); + +G_BEGIN_DECLS +#define GST_TYPE_V4L2_MTK_VPU_DEC \ + (gst_v4l2_mtk_vpu_dec_get_type()) +#define GST_V4L2_MTK_VPU_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_MTK_VPU_DEC,GstV4l2MtkVpuDec)) +#define GST_V4L2_MTK_VPU_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_MTK_VPU_DEC,GstV4l2MtkVpuDecClass)) +#define GST_IS_V4L2_MTK_VPU_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_MTK_VPU_DEC)) +#define GST_IS_V4L2_MTK_VPU_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_MTK_VPU_DEC)) +typedef struct _GstV4l2MtkVpuDec GstV4l2MtkVpuDec; +typedef struct _GstV4l2MtkVpuDecClass GstV4l2MtkVpuDecClass; + +struct _GstV4l2MtkVpuDec +{ + GstV4l2VideoDec parent; +}; + +struct _GstV4l2MtkVpuDecClass +{ + GstV4l2VideoDecClass parent_class; +}; + +GType gst_v4l2_mtk_vpu_dec_get_type (void); + +gboolean gst_v4l2_is_mtk_vpu_dec (GstCaps * sink_caps, GstCaps * src_caps); + +gboolean gst_v4l2_mtk_vpu_dec_register (GstPlugin * plugin, + const gchar * basename, + const gchar * device_path, GstCaps * sink_caps, GstCaps * src_caps); + +G_END_DECLS +#endif /* __GST_V4L2_MTK_VPU_DEC_H__ */ diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c old mode 100644 new mode 100755 index 549e06f..3ac9a87 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -55,7 +55,7 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2_debug); #define DEFAULT_PROP_FREQUENCY 0 #define DEFAULT_PROP_IO_MODE GST_V4L2_IO_AUTO -#define ENCODED_BUFFER_SIZE (1 * 1024 * 1024) +#define ENCODED_BUFFER_SIZE (2 * 1024 * 1024) enum { @@ -182,12 +182,30 @@ static const GstV4L2FormatDesc gst_v4l2_formats[] = { {V4L2_PIX_FMT_VC1_ANNEX_G, FALSE, GST_V4L2_CODEC}, {V4L2_PIX_FMT_VC1_ANNEX_L, FALSE, GST_V4L2_CODEC}, {V4L2_PIX_FMT_VP8, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE}, + {V4L2_PIX_FMT_VP9, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE}, /* Vendor-specific formats */ {V4L2_PIX_FMT_WNVA, TRUE, GST_V4L2_CODEC}, {V4L2_PIX_FMT_SN9C10X, TRUE, GST_V4L2_CODEC}, {V4L2_PIX_FMT_PWC1, TRUE, GST_V4L2_CODEC}, {V4L2_PIX_FMT_PWC2, TRUE, GST_V4L2_CODEC}, + + {V4L2_PIX_FMT_MT21, TRUE, GST_V4L2_RAW}, + {V4L2_PIX_FMT_DIVX, FALSE, GST_V4L2_CODEC}, + {V4L2_PIX_FMT_DIVX3, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE}, + {V4L2_PIX_FMT_DIVX4, FALSE, GST_V4L2_CODEC}, + {V4L2_PIX_FMT_DIVX5, FALSE, GST_V4L2_CODEC}, + {V4L2_PIX_FMT_DIVX6, FALSE, GST_V4L2_CODEC}, + {V4L2_PIX_FMT_S263, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE}, + {V4L2_PIX_FMT_H265, FALSE, GST_V4L2_CODEC}, + /*WMV not parseable */ + {V4L2_PIX_FMT_WMV1, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE}, + {V4L2_PIX_FMT_WMV2, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE}, + {V4L2_PIX_FMT_WMV3, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE}, + + /*realvideo not parseable */ + {V4L2_PIX_FMT_RV30, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE}, + {V4L2_PIX_FMT_RV40, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE}, }; #define GST_V4L2_FORMAT_COUNT (G_N_ELEMENTS (gst_v4l2_formats)) @@ -1000,6 +1018,7 @@ gst_v4l2_object_format_get_rank (const struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_NV61: /* 16 Y/CrCb 4:2:2 */ case V4L2_PIX_FMT_NV61M: /* Same as NV61 */ case V4L2_PIX_FMT_NV24: /* 24 Y/CrCb 4:4:4 */ + case V4L2_PIX_FMT_MT21: rank = YUV_ODD_BASE_RANK; break; @@ -1270,6 +1289,7 @@ gst_v4l2_object_v4l2fourcc_to_video_format (guint32 fourcc) format = GST_VIDEO_FORMAT_YUY2; break; case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YVU420M: format = GST_VIDEO_FORMAT_YV12; break; case V4L2_PIX_FMT_UYVY: @@ -1295,6 +1315,9 @@ gst_v4l2_object_v4l2fourcc_to_video_format (guint32 fourcc) case V4L2_PIX_FMT_NV24: format = GST_VIDEO_FORMAT_NV24; break; + case V4L2_PIX_FMT_MT21: + format = GST_VIDEO_FORMAT_MT21; + break; default: format = GST_VIDEO_FORMAT_UNKNOWN; break; @@ -1344,22 +1367,49 @@ gst_v4l2_object_v4l2fourcc_to_bare_struct (guint32 fourcc) break; case V4L2_PIX_FMT_MPEG1: structure = gst_structure_new ("video/mpeg", - "mpegversion", G_TYPE_INT, 2, NULL); + "mpegversion", G_TYPE_INT, 1, NULL); break; case V4L2_PIX_FMT_MPEG2: structure = gst_structure_new ("video/mpeg", "mpegversion", G_TYPE_INT, 2, NULL); break; case V4L2_PIX_FMT_MPEG4: - case V4L2_PIX_FMT_XVID: structure = gst_structure_new ("video/mpeg", "mpegversion", G_TYPE_INT, 4, "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); break; + case V4L2_PIX_FMT_XVID: + structure = gst_structure_new_empty ("video/x-xvid"); + break; + case V4L2_PIX_FMT_DIVX3: + structure = gst_structure_new("video/x-divx", + "divxversion", G_TYPE_INT, 3, NULL); + break; + case V4L2_PIX_FMT_DIVX4: + structure = gst_structure_new("video/x-divx", + "divxversion", G_TYPE_INT, 4, NULL); + break; + case V4L2_PIX_FMT_DIVX5: + structure = gst_structure_new("video/x-divx", + "divxversion", G_TYPE_INT, 5, NULL); + break; + case V4L2_PIX_FMT_DIVX6: + structure = gst_structure_new("video/x-divx", + "divxversion", G_TYPE_INT, 6, NULL); + break; case V4L2_PIX_FMT_H263: structure = gst_structure_new ("video/x-h263", "variant", G_TYPE_STRING, "itu", NULL); break; + case V4L2_PIX_FMT_S263: + structure = gst_structure_new ("video/x-flash-video", + "flvversion", G_TYPE_INT, 1, NULL); + break; + case V4L2_PIX_FMT_H265: /* H.265 */ + structure = gst_structure_new ("video/x-h265", + "stream-format", G_TYPE_STRING, "byte-stream", "alignment", + G_TYPE_STRING, "au", NULL); + break; case V4L2_PIX_FMT_H264: /* H.264 */ structure = gst_structure_new ("video/x-h264", "stream-format", G_TYPE_STRING, "byte-stream", "alignment", @@ -1378,6 +1428,23 @@ gst_v4l2_object_v4l2fourcc_to_bare_struct (guint32 fourcc) case V4L2_PIX_FMT_VP8: structure = gst_structure_new_empty ("video/x-vp8"); break; + case V4L2_PIX_FMT_VP9: + structure = gst_structure_new_empty ("video/x-vp9"); + break; + case V4L2_PIX_FMT_WMV1: + case V4L2_PIX_FMT_WMV2: + case V4L2_PIX_FMT_WMV3: + case V4L2_PIX_FMT_WMVA: + case V4L2_PIX_FMT_WVC1: + structure = gst_structure_new_empty ("video/x-wmv"); + break; + case V4L2_PIX_FMT_RV30: + case V4L2_PIX_FMT_RV40: + structure = gst_structure_new_empty ("video/x-pn-realvideo"); + break; + case V4L2_PIX_FMT_MT21: + structure = gst_structure_new_empty ("video/x-raw"); + break; case V4L2_PIX_FMT_GREY: /* 8 Greyscale */ case V4L2_PIX_FMT_Y16: case V4L2_PIX_FMT_Y16_BE: @@ -1408,6 +1475,7 @@ gst_v4l2_object_v4l2fourcc_to_bare_struct (guint32 fourcc) case V4L2_PIX_FMT_YUV410: case V4L2_PIX_FMT_YUV420: /* I420/IYUV */ case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_UYVY: @@ -1627,6 +1695,7 @@ gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps, break; case GST_VIDEO_FORMAT_YV12: fourcc = V4L2_PIX_FMT_YVU420; + fourcc_nc = V4L2_PIX_FMT_YVU420M; break; case GST_VIDEO_FORMAT_Y41B: fourcc = V4L2_PIX_FMT_YUV411P; @@ -1754,6 +1823,50 @@ gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps, fourcc = V4L2_PIX_FMT_PWC1; } else if (g_str_equal (mimetype, "video/x-pwc2")) { fourcc = V4L2_PIX_FMT_PWC2; + } else if (g_str_equal (mimetype, "video/x-xvid")) { + fourcc = V4L2_PIX_FMT_XVID; + } else if (g_str_equal (mimetype, "video/x-divx")) { + gint version; + if (gst_structure_get_int (structure, "divxversion", &version)) { + switch (version) { + case 3: + fourcc = V4L2_PIX_FMT_DIVX3; + break; + case 4: + fourcc = V4L2_PIX_FMT_DIVX4; + break; + case 5: + fourcc = V4L2_PIX_FMT_DIVX5; + break; + case 6: + fourcc = V4L2_PIX_FMT_DIVX6; + break; + default: + break; + } + } + } else if (g_str_equal (mimetype, "video/x-flash-video")) { + fourcc = V4L2_PIX_FMT_S263; + } else if (g_str_equal (mimetype, "video/x-h265")) { + fourcc = V4L2_PIX_FMT_H265; + } else if (g_str_equal (mimetype, "video/x-vp9")) { + fourcc = V4L2_PIX_FMT_VP9; + } else if (g_str_equal (mimetype, "video/x-wmv")) { + fourcc = V4L2_PIX_FMT_WMV3; + }else if (g_str_equal (mimetype, "video/x-pn-realvideo")) { + gint version; + if (gst_structure_get_int (structure, "rmversion", &version)) { + switch (version) { + case 3: + fourcc = V4L2_PIX_FMT_RV30; + break; + case 4: + fourcc = V4L2_PIX_FMT_RV40; + break; + default: + break; + } + } } } @@ -2530,6 +2643,9 @@ gst_v4l2_object_probe_caps_for_format (GstV4l2Object * v4l2object, pixelformat, maxw, maxh, template); if (tmp) { + /*the width&height means the buffer size, not the display size, + *the buffer size is align by driver, no need to align in gstreamer. */ +#if 0 GValue step_range = G_VALUE_INIT; g_value_init (&step_range, GST_TYPE_INT_RANGE); @@ -2538,9 +2654,13 @@ gst_v4l2_object_probe_caps_for_format (GstV4l2Object * v4l2object, gst_value_set_int_range_step (&step_range, h, maxh, step_h); gst_structure_take_value (tmp, "height", &step_range); - +#else + gst_structure_set (tmp, "width", GST_TYPE_INT_RANGE, (gint) w, + (gint) maxw, "height", GST_TYPE_INT_RANGE, (gint) h, (gint) maxh, + NULL); /* no point using the results list here, since there's only one struct */ gst_v4l2_object_update_and_append (v4l2object, pixelformat, ret, tmp); +#endif } } else if (size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) { guint32 maxw, maxh; @@ -3584,8 +3704,8 @@ gst_v4l2_object_acquire_format (GstV4l2Object * v4l2object, GstVideoInfo * info) align.padding_top = crop.c.top; align.padding_right = width - crop.c.width - crop.c.left; align.padding_bottom = height - crop.c.height - crop.c.top; - width = crop.c.width; - height = crop.c.height; + //width = crop.c.width; + //height = crop.c.height; } gst_video_info_set_format (info, format, width, height); @@ -4154,3 +4274,435 @@ different_caps: return FALSE; } } + +gboolean +gst_v4l2_object_set_enc_format (GstV4l2Object * v4l2object, GstCaps * caps, gboolean active) +{ + gint fd = v4l2object->video_fd; + struct v4l2_format format; + struct v4l2_streamparm streamparm; + enum v4l2_field field; + guint32 pixelformat; + struct v4l2_fmtdesc *fmtdesc; + GstVideoInfo info; + GstVideoAlignment align; + gint width, height, fps_n, fps_d; + gint n_v4l_planes; + gint i = 0; + gboolean is_mplane; + enum v4l2_colorspace colorspace = 0; + + GST_V4L2_CHECK_OPEN (v4l2object); + if (active) + GST_V4L2_CHECK_NOT_ACTIVE (v4l2object); + + is_mplane = V4L2_TYPE_IS_MULTIPLANAR (v4l2object->type); + + gst_video_info_init (&info); + gst_video_alignment_reset (&align); + + if (!gst_v4l2_object_get_caps_info (v4l2object, caps, &fmtdesc, &info)) + goto invalid_caps; + + pixelformat = fmtdesc->pixelformat; + width = GST_VIDEO_INFO_WIDTH (&info); + height = GST_VIDEO_INFO_HEIGHT (&info); + fps_n = GST_VIDEO_INFO_FPS_N (&info); + fps_d = GST_VIDEO_INFO_FPS_D (&info); + + /* if encoded format (GST_VIDEO_INFO_N_PLANES return 0) + * or if contiguous is prefered */ + n_v4l_planes = GST_VIDEO_INFO_N_PLANES (&info); + /* Rick Chang + Our driver will check the number of planes. Can't change it. + if (!n_v4l_planes || !v4l2object->prefered_non_contiguous) + */ + if (!n_v4l_planes || (!v4l2object->prefered_non_contiguous && !V4L2_TYPE_IS_OUTPUT(v4l2object->type))) + n_v4l_planes = 1; + + if (GST_VIDEO_INFO_IS_INTERLACED (&info)) { + GST_DEBUG_OBJECT (v4l2object->element, "interlaced video"); + /* ideally we would differentiate between types of interlaced video + * but there is not sufficient information in the caps.. + */ + field = V4L2_FIELD_INTERLACED; + } else { + GST_DEBUG_OBJECT (v4l2object->element, "progressive video"); + field = V4L2_FIELD_NONE; + } + + if (V4L2_TYPE_IS_OUTPUT (v4l2object->type)) { + /* We should set colorspace if we have it */ + if (gst_video_colorimetry_matches (&info.colorimetry, "bt601")) { + colorspace = V4L2_COLORSPACE_SMPTE170M; + } else if (gst_video_colorimetry_matches (&info.colorimetry, "bt709")) { + colorspace = V4L2_COLORSPACE_REC709; + } else if (gst_video_colorimetry_matches (&info.colorimetry, "smpte240m")) { + colorspace = V4L2_COLORSPACE_SMPTE240M; + } else { + /* Try to guess colorspace according to pixelformat and size */ + if (GST_VIDEO_INFO_IS_YUV (&info)) { + /* SD streams likely use SMPTE170M and HD streams REC709 */ + if (width <= 720 && height <= 576) + colorspace = V4L2_COLORSPACE_SMPTE170M; + else + colorspace = V4L2_COLORSPACE_REC709; + } else if (GST_VIDEO_INFO_IS_RGB (&info)) { + colorspace = V4L2_COLORSPACE_SRGB; + } + } + } + + GST_DEBUG_OBJECT (v4l2object->element, "Desired format %dx%d, format " + "%" GST_FOURCC_FORMAT " stride: %d", width, height, + GST_FOURCC_ARGS (pixelformat), GST_VIDEO_INFO_PLANE_STRIDE (&info, 0)); + + memset (&format, 0x00, sizeof (struct v4l2_format)); + format.type = v4l2object->type; + + if (is_mplane) { + format.type = v4l2object->type; + format.fmt.pix_mp.pixelformat = pixelformat; + format.fmt.pix_mp.width = width; + format.fmt.pix_mp.height = height; + format.fmt.pix_mp.field = field; + format.fmt.pix_mp.num_planes = n_v4l_planes; + + /* try to ask our prefered stride but it's not a failure if not + * accepted */ + for (i = 0; i < n_v4l_planes; i++) { + gint stride = GST_VIDEO_INFO_PLANE_STRIDE (&info, i); + + if (GST_VIDEO_FORMAT_INFO_IS_TILED (info.finfo)) + stride = GST_VIDEO_TILE_X_TILES (stride) << + GST_VIDEO_FORMAT_INFO_TILE_WS (info.finfo); + + format.fmt.pix_mp.plane_fmt[i].bytesperline = stride; + } + + if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ENCODED) + format.fmt.pix_mp.plane_fmt[0].sizeimage = ENCODED_BUFFER_SIZE; + } else { + gint stride = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0); + + format.type = v4l2object->type; + format.fmt.pix.width = width; + format.fmt.pix.height = height; + format.fmt.pix.pixelformat = pixelformat; + format.fmt.pix.field = field; + + if (GST_VIDEO_FORMAT_INFO_IS_TILED (info.finfo)) + stride = GST_VIDEO_TILE_X_TILES (stride) << + GST_VIDEO_FORMAT_INFO_TILE_WS (info.finfo); + + /* try to ask our prefered stride */ + format.fmt.pix.bytesperline = stride; + + if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ENCODED) + format.fmt.pix.sizeimage = ENCODED_BUFFER_SIZE; + } + + GST_DEBUG_OBJECT (v4l2object->element, "Desired format is %dx%d, format " + "%" GST_FOURCC_FORMAT ", nb planes %d", format.fmt.pix.width, + format.fmt.pix_mp.height, + GST_FOURCC_ARGS (format.fmt.pix.pixelformat), + is_mplane ? format.fmt.pix_mp.num_planes : 1); + +#ifndef GST_DISABLE_GST_DEBUG + if (is_mplane) { + for (i = 0; i < format.fmt.pix_mp.num_planes; i++) + GST_DEBUG_OBJECT (v4l2object->element, " stride %d", + format.fmt.pix_mp.plane_fmt[i].bytesperline); + } else { + GST_DEBUG_OBJECT (v4l2object->element, " stride %d", + format.fmt.pix.bytesperline); + } +#endif + + if (V4L2_TYPE_IS_OUTPUT (v4l2object->type)) { + if (is_mplane) + format.fmt.pix_mp.colorspace = colorspace; + else + format.fmt.pix.colorspace = colorspace; + + GST_DEBUG_OBJECT (v4l2object->element, "Desired colorspace is %d", + colorspace); + } + + if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) + goto set_fmt_failed; + + GST_DEBUG_OBJECT (v4l2object->element, "Got format of %dx%d, format " + "%" GST_FOURCC_FORMAT ", nb planes %d, colorspace %d", + format.fmt.pix.width, format.fmt.pix_mp.height, + GST_FOURCC_ARGS (format.fmt.pix.pixelformat), + is_mplane ? format.fmt.pix_mp.num_planes : 1, + is_mplane ? format.fmt.pix_mp.colorspace : format.fmt.pix.colorspace); + +#ifndef GST_DISABLE_GST_DEBUG + if (is_mplane) { + for (i = 0; i < format.fmt.pix_mp.num_planes; i++) + GST_DEBUG_OBJECT (v4l2object->element, " stride %d, sizeimage %d", + format.fmt.pix_mp.plane_fmt[i].bytesperline, + format.fmt.pix_mp.plane_fmt[i].sizeimage); + } else { + GST_DEBUG_OBJECT (v4l2object->element, " stride %d, sizeimage %d", + format.fmt.pix.bytesperline, format.fmt.pix.sizeimage); + } +#endif + + if (format.fmt.pix.pixelformat != pixelformat) + goto invalid_pixelformat; + + /* Only negotiate size with raw data. + * For some codecs the dimensions are *not* in the bitstream, IIRC VC1 + * in ASF mode for example, there is also not reason for a driver to + * change the size. */ + if (info.finfo->format != GST_VIDEO_FORMAT_ENCODED) { + /* We can crop larger images */ + if (format.fmt.pix.width < width || format.fmt.pix.height < height) + goto invalid_dimensions; + + /* Note, this will be adjusted if upstream has non-centered cropping. */ + align.padding_top = 0; + align.padding_bottom = format.fmt.pix.height - height; + align.padding_left = 0; + align.padding_right = format.fmt.pix.width - width; + } + + if (is_mplane && format.fmt.pix_mp.num_planes != n_v4l_planes) + goto invalid_planes; + + if (GST_VIDEO_INFO_HAS_ALPHA (&info)) { + struct v4l2_control ctl = { 0, }; + ctl.id = V4L2_CID_ALPHA_COMPONENT; + ctl.value = 0xff; + + if (v4l2_ioctl (fd, VIDIOC_S_CTRL, &ctl) < 0) + GST_WARNING_OBJECT (v4l2object->element, + "Failed to set alpha component value"); + } + + if (V4L2_TYPE_IS_OUTPUT (v4l2object->type)) { + gint bitrate = 0; + gint gop = 0; + gint prepend_hdr = 0; + struct v4l2_control gop_ctl = { 0, }; + struct v4l2_control bps_ctl = { 0, }; + struct v4l2_control prepend_hdr_ctl = { 0, }; + GstStructure *st; + + st = gst_caps_get_structure (caps, 0); + if (gst_structure_has_field (st, "bitrate")) { + gst_structure_get_int(st, "bitrate", &bitrate); + } + + if (gst_structure_has_field (st, "gop")) { + gst_structure_get_int(st, "gop", &gop); + } + + if (gst_structure_has_field (st, "prepend_hdr")) { + gst_structure_get_int(st, "prepend_hdr", &prepend_hdr); + } + + bps_ctl.id = V4L2_CID_MPEG_VIDEO_BITRATE; + bps_ctl.value = bitrate; + if (v4l2_ioctl (fd, VIDIOC_S_CTRL, &bps_ctl) < 0) { + GST_WARNING_OBJECT (v4l2object->element, + "Failed to set bps_ctl component value"); + } + + gop_ctl.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE; + gop_ctl.value = gop; + if (v4l2_ioctl (fd, VIDIOC_S_CTRL, &gop_ctl) < 0) { + GST_WARNING_OBJECT (v4l2object->element, + "Failed to set gop_ctl component value"); + } + + prepend_hdr_ctl.id = V4L2_CID_MPEG_VIDEO_HEADER_MODE; + prepend_hdr_ctl.value = prepend_hdr; + if (v4l2_ioctl (fd, VIDIOC_S_CTRL, &prepend_hdr_ctl) < 0) { + GST_WARNING_OBJECT (v4l2object->element, + "Failed to set prepend_hdr_ctl component value"); + } + + GST_INFO_OBJECT (v4l2object->element, "bitrate = %d, gop=%d, prepend_hdr=%d", + bitrate, gop, prepend_hdr); + } + + /* Is there a reason we require the caller to always specify a framerate? */ + GST_DEBUG_OBJECT (v4l2object->element, "Desired framerate: %u/%u", fps_n, + fps_d); + + memset (&streamparm, 0x00, sizeof (struct v4l2_streamparm)); + streamparm.type = v4l2object->type; + + /* Rick Chang + In encode flow, the frame rate is decided by client not driver. + */ +#if 0 + if (v4l2_ioctl (fd, VIDIOC_G_PARM, &streamparm) < 0) + goto get_parm_failed; + + GST_VIDEO_INFO_FPS_N (&info) = + streamparm.parm.capture.timeperframe.denominator; + GST_VIDEO_INFO_FPS_D (&info) = streamparm.parm.capture.timeperframe.numerator; + + if (v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE + || v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + GST_DEBUG_OBJECT (v4l2object->element, "Got framerate: %u/%u", + streamparm.parm.capture.timeperframe.denominator, + streamparm.parm.capture.timeperframe.numerator); + + /* We used to skip frame rate setup if the camera was already setup + * with the requested frame rate. This breaks some cameras though, + * causing them to not output data (several models of Thinkpad cameras + * have this problem at least). + * So, don't skip. */ + GST_LOG_OBJECT (v4l2object->element, "Setting framerate to %u/%u", fps_n, + fps_d); + /* We want to change the frame rate, so check whether we can. Some cheap USB + * cameras don't have the capability */ + if ((streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) == 0) { + GST_DEBUG_OBJECT (v4l2object->element, + "Not setting framerate (not supported)"); + goto done; + } +#endif + if (V4L2_TYPE_IS_OUTPUT (v4l2object->type)) { + /* Note: V4L2 wants the frame interval, we have the frame rate */ + streamparm.parm.capture.timeperframe.numerator = fps_d; + streamparm.parm.capture.timeperframe.denominator = fps_n; + + /* some cheap USB cam's won't accept any change */ + if (v4l2_ioctl (fd, VIDIOC_S_PARM, &streamparm) < 0) + goto set_parm_failed; + + /* get new values */ + fps_d = streamparm.parm.capture.timeperframe.numerator; + fps_n = streamparm.parm.capture.timeperframe.denominator; + + GST_INFO_OBJECT (v4l2object->element, "Set framerate to %u/%u", fps_n, + fps_d); + + GST_VIDEO_INFO_FPS_N (&info) = fps_n; + GST_VIDEO_INFO_FPS_D (&info) = fps_d; + } +#if 0 + } +#endif + +done: + if (!active) + return TRUE; + + /* add boolean return, so we can fail on drivers bugs */ + gst_v4l2_object_save_format (v4l2object, fmtdesc, &format, &info, &align); + + /* now configure the pool */ + if (!gst_v4l2_object_setup_pool (v4l2object, caps)) + goto pool_failed; + + return TRUE; + + /* ERRORS */ +invalid_caps: + { + GST_DEBUG_OBJECT (v4l2object->element, "can't parse caps %" GST_PTR_FORMAT, + caps); + return FALSE; + } +set_fmt_failed: + { + if (errno == EBUSY) { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, BUSY, + (_("Device '%s' is busy"), v4l2object->videodev), + ("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s", + GST_FOURCC_ARGS (pixelformat), width, height, + g_strerror (errno))); + } else { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Device '%s' cannot capture at %dx%d"), + v4l2object->videodev, width, height), + ("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s", + GST_FOURCC_ARGS (pixelformat), width, height, + g_strerror (errno))); + } + return FALSE; + } +invalid_dimensions: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Device '%s' cannot capture at %dx%d"), + v4l2object->videodev, width, height), + ("Tried to capture at %dx%d, but device returned size %dx%d", + width, height, format.fmt.pix.width, format.fmt.pix.height)); + return FALSE; + } +invalid_pixelformat: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Device '%s' cannot capture in the specified format"), + v4l2object->videodev), + ("Tried to capture in %" GST_FOURCC_FORMAT + ", but device returned format" " %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (pixelformat), + GST_FOURCC_ARGS (format.fmt.pix.pixelformat))); + return FALSE; + } +invalid_planes: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Device '%s' does support non-contiguous planes"), + v4l2object->videodev), + ("Device wants %d planes", format.fmt.pix_mp.num_planes)); + return FALSE; + } +get_parm_failed: + { + /* it's possible that this call is not supported */ + if (errno != EINVAL && errno != ENOTTY) { + GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS, + (_("Could not get parameters on device '%s'"), + v4l2object->videodev), GST_ERROR_SYSTEM); + } + goto done; + } +set_parm_failed: + { + GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS, + (_("Video device did not accept new frame rate setting.")), + GST_ERROR_SYSTEM); + goto done; + } +pool_failed: + { + /* setup_pool already send the error */ + return FALSE; + } +} + +gboolean +gst_v4l2_object_get_crop (GstV4l2Object * obj, guint *crop_width, guint *crop_height) +{ + struct v4l2_crop crop = { 0 }; + + if ((crop_width == NULL) || (crop_height == NULL)) + return FALSE; + + crop.type = obj->type; + + if (v4l2_ioctl (obj->video_fd, VIDIOC_G_CROP, &crop) < 0) { + GST_WARNING_OBJECT (obj->element, "VIDIOC_G_CROP failed"); + return FALSE; + } + *crop_width = crop.c.width; + *crop_height = crop.c.height; + + GST_INFO_OBJECT (obj->element, + "Got cropping left %u, top %u, size %ux%u", crop.c.left, crop.c.top, + crop.c.width, crop.c.height); + + return TRUE; +} + diff --git a/sys/v4l2/gstv4l2object.h b/sys/v4l2/gstv4l2object.h old mode 100644 new mode 100755 index 7117d43..8d814fb --- a/sys/v4l2/gstv4l2object.h +++ b/sys/v4l2/gstv4l2object.h @@ -271,6 +271,11 @@ gboolean gst_v4l2_object_acquire_format (GstV4l2Object * v4l2object, gboolean gst_v4l2_object_set_crop (GstV4l2Object * obj); +gboolean gst_v4l2_object_get_crop (GstV4l2Object * obj, guint *crop_width, guint *crop_height); + +gboolean gst_v4l2_object_set_enc_format (GstV4l2Object * v4l2object, GstCaps * caps, gboolean active); + + gboolean gst_v4l2_object_decide_allocation (GstV4l2Object * v4l2object, GstQuery * query); diff --git a/sys/v4l2/gstv4l2src.c b/sys/v4l2/gstv4l2src.c old mode 100644 new mode 100755 index a6d34b1..7b06554 --- a/sys/v4l2/gstv4l2src.c +++ b/sys/v4l2/gstv4l2src.c @@ -62,7 +62,7 @@ GST_DEBUG_CATEGORY (v4l2src_debug); #define GST_CAT_DEFAULT v4l2src_debug -#define DEFAULT_PROP_DEVICE "/dev/video0" +#define DEFAULT_PROP_DEVICE "/dev/video5" enum { diff --git a/sys/v4l2/gstv4l2videodec.c b/sys/v4l2/gstv4l2videodec.c old mode 100644 new mode 100755 index 8ca5e38..9992cc4 --- a/sys/v4l2/gstv4l2videodec.c +++ b/sys/v4l2/gstv4l2videodec.c @@ -35,9 +35,20 @@ #include #include +#include +#include GST_DEBUG_CATEGORY_STATIC (gst_v4l2_video_dec_debug); #define GST_CAT_DEFAULT gst_v4l2_video_dec_debug +#define FPS_COUNT_NUM 120 + +static gint fps_count = 0; +static gint64 time_start = 0; +static gint64 time_end = 0; + +static gint64 time_pre_frame = 0; +static guint32 video_count = 0; + static gboolean gst_v4l2_video_dec_flush (GstVideoDecoder * decoder); typedef struct @@ -113,6 +124,9 @@ static gboolean gst_v4l2_video_dec_open (GstVideoDecoder * decoder) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); + GstCaps *src_caps, *caps; + GstElementClass *element_class = GST_ELEMENT_GET_CLASS (decoder); + GstPadTemplate *pad_template; GST_DEBUG_OBJECT (self, "Opening"); @@ -128,8 +142,15 @@ gst_v4l2_video_dec_open (GstVideoDecoder * decoder) if (gst_caps_is_empty (self->probed_sinkcaps)) goto no_encoded_format; - self->probed_srccaps = gst_v4l2_object_get_caps (self->v4l2capture, + src_caps = gst_v4l2_object_get_caps (self->v4l2capture, gst_v4l2_object_get_raw_caps ()); + pad_template = gst_element_class_get_pad_template (element_class, "src"); + caps = gst_pad_template_get_caps (pad_template); + self->probed_srccaps = gst_caps_intersect_full (src_caps, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (src_caps); + gst_caps_unref (caps); + + GST_INFO_OBJECT (self, "probed src caps: %" GST_PTR_FORMAT, self->probed_srccaps); if (gst_caps_is_empty (self->probed_srccaps)) goto no_raw_format; @@ -271,6 +292,7 @@ gst_v4l2_video_dec_flush (GstVideoDecoder * decoder) gst_v4l2_object_unlock (self->v4l2output); gst_v4l2_object_unlock (self->v4l2capture); + g_atomic_int_set (&self->processing, FALSE); gst_pad_stop_task (decoder->srcpad); GST_VIDEO_DECODER_STREAM_LOCK (decoder); } @@ -408,6 +430,103 @@ gst_v4l2_video_dec_get_oldest_frame (GstVideoDecoder * decoder) return frame; } +int gst_v4l2_sub_event(GstV4l2Object * v4l2object) +{ + int r = 0;; + struct v4l2_event_subscription sub = { 0 }; + + sub.type = V4L2_EVENT_SOURCE_CHANGE; + r = v4l2_ioctl(v4l2object->video_fd, VIDIOC_SUBSCRIBE_EVENT, &sub); + + return 0; +} + +static gboolean gst_v4l2_check_res_change(GstV4l2Object * v4l2object) +{ + struct v4l2_event e = { 0 }; + int r; + __u32 change; + + GST_LOG("ENTER gst_v4l2_check_res_change"); + r = v4l2_ioctl(v4l2object->video_fd, VIDIOC_DQEVENT, &e); + + GST_LOG("e.type=%d",e.type); + if (e.type != V4L2_EVENT_SOURCE_CHANGE) + goto end; + + change = e.u.src_change.changes; + if (change & V4L2_EVENT_SRC_CH_RESOLUTION) { + GST_INFO("Got resolution change,change=%d",change); + return TRUE; + } +end: + return FALSE; +} + +static void +gst_v4l2_update_caps (GstVideoDecoder * decoder, guint width, guint height, guint crop_width, guint crop_height) +{ + GstCaps *prevcaps = NULL; + GstCaps *updatecaps = NULL; + GstStructure *s = NULL; + + prevcaps = gst_pad_get_current_caps (decoder->srcpad); + + if (prevcaps) { + gboolean ret = TRUE; + gboolean res_changed = FALSE; + gint disp_width = 0; + gint disp_height = 0; + gint pre_width = 0; + gint pre_height = 0; + + s = gst_caps_get_structure (prevcaps, 0); + if (s && gst_structure_has_field (s, "display_width")) + gst_structure_get_int (s, "display_width", &disp_width); + + if (s && gst_structure_has_field (s, "display_height")) + gst_structure_get_int (s, "display_height", &disp_height); + + if (s && gst_structure_has_field (s, "width")) + gst_structure_get_int (s, "width", &pre_width); + + if (s && gst_structure_has_field (s, "height")) + gst_structure_get_int (s, "height", &pre_height); + + GST_INFO("display_width=%d,display_height=%d,crop.width=%d,crop.height=%d,prewidth=%d,preheight=%d,width=%d,height=%d", + disp_width, disp_height, crop_width, crop_height, pre_width, pre_height, width, height); + + updatecaps = gst_caps_copy_nth (prevcaps, 0); + + if ((crop_width != disp_width) || (crop_height != disp_height)) { + res_changed = TRUE; + gst_caps_set_simple (updatecaps, "display_width", G_TYPE_INT, crop_width, NULL); + gst_caps_set_simple (updatecaps, "display_height", G_TYPE_INT, crop_height, NULL); + } + + if ((pre_width != width) || (pre_height != height)) { + res_changed = TRUE; + gst_caps_set_simple (updatecaps, "width", G_TYPE_INT, width, NULL); + gst_caps_set_simple (updatecaps, "height", G_TYPE_INT, height, NULL); + } + + if (res_changed == TRUE) { + GstVideoCodecState *state = NULL; + state = gst_video_decoder_get_output_state(decoder); + state->caps = updatecaps; + ret = gst_pad_set_caps (decoder->srcpad, updatecaps); + if (ret == FALSE){ + GST_INFO("gst_pad_set_caps FAILED"); + } + } + + if (prevcaps) + gst_caps_unref (prevcaps); + if (updatecaps) + gst_caps_unref (updatecaps); + } + return; +} static void gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) { @@ -417,6 +536,10 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) GstVideoCodecFrame *frame; GstBuffer *buffer = NULL; GstFlowReturn ret; + struct pollfd pfd; + short wait_event = V4L2_TYPE_IS_OUTPUT(self->v4l2capture->type) ? POLLOUT : POLLIN; + int r = 0; + gboolean res_changed = FALSE; GST_LOG_OBJECT (decoder, "Allocate output buffer"); @@ -425,6 +548,53 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) * stream lock. we know that the acquire may need to poll until more frames * comes in and holding this lock would prevent that. */ + pfd.fd = self->v4l2capture->video_fd; + pfd.events = POLLERR; + pfd.events |= wait_event; + + if (!V4L2_TYPE_IS_OUTPUT(self->v4l2capture->type)) + pfd.events |= POLLPRI; + + GST_LOG ("before poll"); + r = poll(&pfd, 1, 0); + GST_LOG ("after poll,pfd.revents=%d",pfd.revents); + + if (pfd.revents & POLLPRI) + res_changed = gst_v4l2_check_res_change(self->v4l2capture); + + if (res_changed == TRUE) { + GstVideoInfo info; + guint crop_width = 0; + guint crop_height = 0; + + if (self->v4l2capture->pool) { + GST_INFO_OBJECT (decoder, "deactivating pool"); + gst_buffer_pool_set_active (self->v4l2capture->pool, FALSE); + } + + if (!gst_v4l2_object_acquire_format (self->v4l2capture, &info)) { + GST_INFO_OBJECT (decoder, "gst_v4l2_object_acquire_format failed"); + goto beach; + } + + if (TRUE == gst_v4l2_object_get_crop(self->v4l2capture, &crop_width, &crop_height)) { + gst_v4l2_update_caps(decoder, info.width, info.height, crop_width, crop_height); + } + else { + GST_WARNING_OBJECT (decoder, "gst_v4l2_object_get_crop failed"); + goto beach; + } + + if (!gst_video_decoder_negotiate (decoder)) { + GST_ERROR_OBJECT (decoder, "negotiate error"); + goto beach; + } + + if (self->v4l2capture->pool) { + GST_INFO_OBJECT (decoder, "activating pool"); + gst_buffer_pool_set_active (GST_BUFFER_POOL (self->v4l2capture->pool), TRUE); + } + } pool = gst_video_decoder_get_buffer_pool (decoder); /* Pool may be NULL if we started going to READY state */ @@ -451,9 +621,34 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) if (frame) { frame->output_buffer = buffer; + if (GST_BUFFER_TIMESTAMP (buffer) != 0 && GST_BUFFER_TIMESTAMP (buffer) != (((GstClockTime)-1) - 999)) { + frame->pts = GST_BUFFER_TIMESTAMP (buffer); + } buffer = NULL; ret = gst_video_decoder_finish_frame (decoder, frame); + gint64 fps_time = 0; + gfloat fps = 0; + + if (fps_count == 0) { + time_start = g_get_monotonic_time(); + } + + fps_count++; + if (fps_count == FPS_COUNT_NUM) { + time_end = g_get_monotonic_time(); + fps_time = time_end - time_start; + fps = FPS_COUNT_NUM * 1000000.0 / fps_time; + GST_INFO_OBJECT (decoder, "fps = %f", fps); + fps_count = 0; + } + + video_count++; + gint64 time_cur = g_get_monotonic_time(); + GST_DEBUG_OBJECT (decoder, "[%d] frame time %lld us \n", + video_count, (time_cur - time_pre_frame)); + time_pre_frame = time_cur; + if (ret != GST_FLOW_OK) goto beach; } else { @@ -515,6 +710,7 @@ gst_v4l2_video_remove_padding(GstCapsFeatures * features, return TRUE; } +gboolean polling_flag = FALSE; static GstFlowReturn gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) @@ -524,6 +720,9 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, GstFlowReturn ret = GST_FLOW_OK; gboolean processed = FALSE; GstBuffer *tmp; + GstElementClass *element_class = GST_ELEMENT_GET_CLASS (decoder); + GstPadTemplate *pad_template; + int r = 0; GST_DEBUG_OBJECT (self, "Handling frame %d", frame->system_frame_number); @@ -541,7 +740,9 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) { GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool); GstVideoInfo info; + GstVideoInfo input_info; GstVideoCodecState *output_state; + GstVideoCodecState *input_state; GstBuffer *codec_data; GstCaps *acquired_caps, *available_caps, *caps, *filter; GstStructure *st; @@ -592,9 +793,10 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, /* Create caps from the acquired format, remove the format field */ acquired_caps = gst_video_info_to_caps (&info); + if (self->v4l2capture->format.fmt.pix.pixelformat == V4L2_PIX_FMT_MT21) { st = gst_caps_get_structure (acquired_caps, 0); gst_structure_remove_field (st, "format"); - + } /* Probe currently available pixel formats */ available_caps = gst_v4l2_object_probe_caps (self->v4l2capture, NULL); available_caps = gst_caps_make_writable (available_caps); @@ -633,6 +835,22 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, /* Copy the rest of the information, there might be more in the future */ output_state->info.interlace_mode = info.interlace_mode; + + input_state = self->input_state; + if (!input_state) { + GST_ERROR_OBJECT (self, "input_state is null"); + } else { + gst_video_info_from_caps(&input_info, input_state->caps); + GST_DEBUG_OBJECT (self, "input_info.width=%d input_info.height=%d", input_info.width, input_info.height); + } + + if (output_state->caps == NULL) { + output_state->caps = gst_video_info_to_caps (&output_state->info); + } + + gst_caps_set_simple (output_state->caps, "display_width", G_TYPE_INT, input_info.width, NULL); + gst_caps_set_simple (output_state->caps, "display_height", G_TYPE_INT, input_info.height, NULL); + gst_video_codec_state_unref (output_state); if (!gst_video_decoder_negotiate (decoder)) { @@ -662,6 +880,7 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, /* Start the processing task, when it quits, the task will disable input * processing to unlock input if draining, or prevent potential block */ g_atomic_int_set (&self->processing, TRUE); + r = gst_v4l2_sub_event(self->v4l2capture); if (!gst_pad_start_task (decoder->srcpad, (GstTaskFunction) gst_v4l2_video_dec_loop, self, (GDestroyNotify) gst_v4l2_video_dec_loop_stopped)) @@ -886,6 +1105,8 @@ static void gst_v4l2_video_dec_init (GstV4l2VideoDec * self) { /* V4L2 object are created in subinstance_init */ + gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST + (self), TRUE); } static void diff --git a/sys/v4l2/gstv4l2videoenc.c b/sys/v4l2/gstv4l2videoenc.c new file mode 100755 index 0000000..e8bb146 --- /dev/null +++ b/sys/v4l2/gstv4l2videoenc.c @@ -0,0 +1,958 @@ +/* + * Copyright (C) 2014 ayaka + * Copyright (C) 2016 Rick Chang + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "gstv4l2videoenc.h" +#include "v4l2_calls.h" + +#include +#include + +#define DEFAULT_PROP_DEVICE "/dev/video1" + +GST_DEBUG_CATEGORY_STATIC (gst_v4l2_video_enc_debug); +#define GST_CAT_DEFAULT gst_v4l2_video_enc_debug + +#define MAX_CODEC_FRAME (2 * 1024 * 1024) + +static gboolean gst_v4l2_video_enc_flush (GstVideoEncoder * encoder); + +enum +{ + PROP_0, + V4L2_STD_OBJECT_PROPS, + PROP_BITRATE, + PROP_GOP, + PROP_PREPEND_HDR, +}; + +#define gst_v4l2_video_enc_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE (GstV4l2VideoEnc, gst_v4l2_video_enc, + GST_TYPE_VIDEO_ENCODER); + +void +gst_v4l2_video_enc_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (object); + + switch (prop_id) { + /* Split IO mode so output is configure through 'io-mode' and capture + * through 'capture-io-mode' */ + case PROP_IO_MODE: + gst_v4l2_object_set_property_helper (self->v4l2output, + PROP_IO_MODE, value, pspec); + break; + case PROP_CAPTURE_IO_MODE: + gst_v4l2_object_set_property_helper (self->v4l2capture, + prop_id, value, pspec); + break; + + case PROP_DEVICE: + gst_v4l2_object_set_property_helper (self->v4l2output, + prop_id, value, pspec); + gst_v4l2_object_set_property_helper (self->v4l2capture, + prop_id, value, pspec); + break; + case PROP_BITRATE: + self->bitrate = g_value_get_int (value); + break; + case PROP_GOP: + self->gop = g_value_get_int (value); + break; + case PROP_PREPEND_HDR: + self->prepend_hdr = g_value_get_int (value); + break; + + /* By default, only set on output */ + default: + if (!gst_v4l2_object_set_property_helper (self->v4l2output, + prop_id, value, pspec)) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + break; + } +} + +void +gst_v4l2_video_enc_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (object); + + switch (prop_id) { + case PROP_IO_MODE: + gst_v4l2_object_get_property_helper (self->v4l2output, prop_id, value, + pspec); + break; + case PROP_CAPTURE_IO_MODE: + gst_v4l2_object_get_property_helper (self->v4l2output, PROP_IO_MODE, + value, pspec); + break; + case PROP_BITRATE: + g_value_set_int (value, self->bitrate); + break; + case PROP_GOP: + g_value_set_int (value, self->gop); + break; + case PROP_PREPEND_HDR: + g_value_set_int (value, self->prepend_hdr); + break; + + /* By default read from output */ + default: + if (!gst_v4l2_object_get_property_helper (self->v4l2output, + prop_id, value, pspec)) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + break; + } +} + +static gboolean +gst_v4l2_video_enc_open (GstVideoEncoder * encoder) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + + GST_DEBUG_OBJECT (self, "Opening"); + + if (!gst_v4l2_object_open (self->v4l2output)) + goto failure; + + if (!gst_v4l2_object_open_shared (self->v4l2capture, self->v4l2output)) + goto failure; + + self->probed_sinkcaps = gst_v4l2_object_get_caps (self->v4l2output, + gst_v4l2_object_get_raw_caps ()); + + if (gst_caps_is_empty (self->probed_sinkcaps)) + goto no_raw_format; + + self->probed_srccaps = gst_v4l2_object_get_caps (self->v4l2capture, + gst_v4l2_object_get_codec_caps ()); + + if (gst_caps_is_empty (self->probed_srccaps)) + goto no_encoded_format; + + return TRUE; + +no_encoded_format: + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, + (_("Encoder on device %s has no supported output format"), + self->v4l2output->videodev), (NULL)); + goto failure; + + +no_raw_format: + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, + (_("Encoder on device %s has no supported input format"), + self->v4l2output->videodev), (NULL)); + goto failure; + +failure: + if (GST_V4L2_IS_OPEN (self->v4l2output)) + gst_v4l2_object_close (self->v4l2output); + + if (GST_V4L2_IS_OPEN (self->v4l2capture)) + gst_v4l2_object_close (self->v4l2capture); + + gst_caps_replace (&self->probed_srccaps, NULL); + gst_caps_replace (&self->probed_sinkcaps, NULL); + + return FALSE; +} + +static gboolean +gst_v4l2_video_enc_close (GstVideoEncoder * encoder) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + + GST_DEBUG_OBJECT (self, "Closing"); + + gst_v4l2_object_close (self->v4l2output); + gst_v4l2_object_close (self->v4l2capture); + gst_caps_replace (&self->probed_srccaps, NULL); + gst_caps_replace (&self->probed_sinkcaps, NULL); + + return TRUE; +} + +static gboolean +gst_v4l2_video_enc_start (GstVideoEncoder * encoder) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + + GST_DEBUG_OBJECT (self, "Starting"); + + g_atomic_int_set(&self->finish, FALSE); + gst_v4l2_object_unlock (self->v4l2output); + g_atomic_int_set (&self->active, TRUE); + self->output_flow = GST_FLOW_OK; + + return TRUE; +} + +static gboolean +gst_v4l2_video_enc_stop (GstVideoEncoder * encoder) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + + GST_DEBUG_OBJECT (self, "Stopping"); + + gst_v4l2_object_unlock (self->v4l2output); + gst_v4l2_object_unlock (self->v4l2capture); + + /* Wait for capture thread to stop */ + gst_pad_stop_task (encoder->srcpad); + + GST_VIDEO_ENCODER_STREAM_LOCK (encoder); + self->output_flow = GST_FLOW_OK; + GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder); + + /* Should have been flushed already */ + g_assert (g_atomic_int_get (&self->active) == FALSE); + g_assert (g_atomic_int_get (&self->processing) == FALSE); + + gst_v4l2_object_stop (self->v4l2output); + gst_v4l2_object_stop (self->v4l2capture); + + if (self->input_state) { + gst_video_codec_state_unref (self->input_state); + self->input_state = NULL; + } + + GST_DEBUG_OBJECT (self, "Stopped"); + + return TRUE; +} + +static gboolean +gst_v4l2_video_enc_set_format (GstVideoEncoder * encoder, + GstVideoCodecState * state) +{ + gboolean ret = TRUE; + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + + GST_DEBUG_OBJECT (self, "Setting format: %" GST_PTR_FORMAT, state->caps); + + if (self->input_state) { + if (gst_v4l2_object_caps_equal (self->v4l2output, state->caps)) { + GST_DEBUG_OBJECT (self, "Compatible caps"); + goto done; + } + gst_video_codec_state_unref (self->input_state); + self->input_state = NULL; + + /* FIXME we probably need to do more work if pools are active */ + } + + state->caps = gst_caps_make_writable (state->caps); + gst_caps_set_simple (state->caps, "bitrate", G_TYPE_INT, self->bitrate, NULL); + gst_caps_set_simple (state->caps, "gop", G_TYPE_INT, self->gop, NULL); + gst_caps_set_simple (state->caps, "prepend_hdr", G_TYPE_INT, self->prepend_hdr, NULL); + + ret = gst_v4l2_object_set_enc_format (self->v4l2output, state->caps, FALSE); + + if (ret) + self->input_state = gst_video_codec_state_ref (state); + + GST_DEBUG_OBJECT (self, "output caps: %" GST_PTR_FORMAT, state->caps); + +done: + return ret; +} + +static gboolean +gst_v4l2_video_enc_flush (GstVideoEncoder * encoder) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + + GST_DEBUG_OBJECT (self, "Flushing"); + + /* Ensure the processing thread has stopped for the reverse playback + * iscount case */ + if (g_atomic_int_get (&self->processing)) { + GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder); + + gst_v4l2_object_unlock_stop (self->v4l2output); + gst_v4l2_object_unlock_stop (self->v4l2capture); + gst_pad_stop_task (encoder->srcpad); + + GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder); + + } + + self->output_flow = GST_FLOW_OK; + + gst_v4l2_object_unlock_stop (self->v4l2output); + gst_v4l2_object_unlock_stop (self->v4l2capture); + + return TRUE; +} + +static gboolean +gst_v4l2_video_enc_negotiate (GstVideoEncoder * encoder) +{ + return GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder); +} + +static GstFlowReturn +gst_v4l2_video_enc_finish (GstVideoEncoder * encoder) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *buffer; + gint i; + + if (!g_atomic_int_get (&self->processing)) + goto done; + + GST_DEBUG_OBJECT (self, "Finishing encoding"); + + /* Keep queuing empty buffers until the processing thread has stopped, + * _pool_process() will return FLUSHING when that happened */ + GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder); + /* + while (ret == GST_FLOW_OK) { + buffer = gst_buffer_new (); + ret = + gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL + (self->v4l2output->pool), &buffer); + gst_buffer_unref (buffer); + } + */ + g_atomic_int_set(&self->finish, TRUE); + for (i = 0; g_atomic_int_get (&self->processing) && i < 100; i++) + g_usleep(1000); + GST_INFO ("Close task. (%d)",i); + + /* and ensure the processing thread has stopped in case another error + * occured. */ + gst_v4l2_object_unlock (self->v4l2capture); + gst_pad_stop_task (encoder->srcpad); + GST_VIDEO_ENCODER_STREAM_LOCK (encoder); + + if (ret == GST_FLOW_FLUSHING) + ret = self->output_flow; + + GST_DEBUG_OBJECT (encoder, "Done draining buffers"); + +done: + return ret; +} + +static GstVideoCodecFrame * +gst_v4l2_video_enc_get_oldest_frame (GstVideoEncoder * encoder) +{ + GstVideoCodecFrame *frame = NULL; + GList *frames, *l; + gint count = 0; + + frames = gst_video_encoder_get_frames (encoder); + + for (l = frames; l != NULL; l = l->next) { + GstVideoCodecFrame *f = l->data; + + if (!frame || frame->pts > f->pts) + frame = f; + + count++; + } + + if (frame) { + GST_LOG_OBJECT (encoder, + "Oldest frame is %d %" GST_TIME_FORMAT + " and %d frames left", + frame->system_frame_number, GST_TIME_ARGS (frame->pts), count - 1); + gst_video_codec_frame_ref (frame); + } + + g_list_free_full (frames, (GDestroyNotify) gst_video_codec_frame_unref); + + return frame; +} + +static void +gst_v4l2_video_enc_loop (GstVideoEncoder * encoder) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + GstVideoCodecFrame *frame; + GstBuffer *buffer = NULL; + GstFlowReturn ret; + gint i; + + frame = gst_v4l2_video_enc_get_oldest_frame (encoder); + if (!frame) { + if (g_atomic_int_get (&self->finish)) + goto beach; + GST_WARNING ("input too slow"); + g_usleep(1000); + return; + } + + GST_LOG_OBJECT (encoder, "Allocate output buffer"); + + buffer = gst_video_encoder_allocate_output_buffer (encoder, MAX_CODEC_FRAME); + if (NULL == buffer) { + ret = GST_FLOW_FLUSHING; + goto beach; + } + + if (frame->system_frame_number == 0) { + GList *header = NULL; + + GST_INFO ("send header"); + ret = + gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL + (self->v4l2capture->pool), &buffer); + if (ret != GST_FLOW_OK) { + gst_buffer_unref (buffer); + goto beach; + } + header = g_list_prepend (header, buffer); + gst_video_encoder_set_headers (encoder, header); + buffer = gst_video_encoder_allocate_output_buffer (encoder, MAX_CODEC_FRAME); + if (NULL == buffer) { + ret = GST_FLOW_FLUSHING; + goto beach; + } + } + + /* FIXME Check if buffer isn't the last one here */ + + GST_LOG_OBJECT (encoder, "Process output buffer"); + ret = + gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL + (self->v4l2capture->pool), &buffer); + + if (ret != GST_FLOW_OK) + goto beach; + + /* if (frame) { */ + frame->output_buffer = buffer; + buffer = NULL; + ret = gst_video_encoder_finish_frame (encoder, frame); + + if (ret != GST_FLOW_OK) + goto beach; + /* + } else { + GST_WARNING_OBJECT (encoder, "Encoder is producing too many buffers"); + gst_buffer_unref (buffer); + } + */ + return; + +beach: + GST_DEBUG_OBJECT (encoder, "Leaving output thread"); + + gst_buffer_replace (&buffer, NULL); + self->output_flow = ret; + g_atomic_int_set (&self->processing, FALSE); + gst_v4l2_object_unlock (self->v4l2output); + gst_pad_pause_task (encoder->srcpad); +} + +static void +gst_v4l2_video_enc_loop_stopped (GstV4l2VideoEnc * self) +{ + if (g_atomic_int_get (&self->processing)) { + GST_DEBUG_OBJECT (self, "Early stop of encoding thread"); + self->output_flow = GST_FLOW_FLUSHING; + g_atomic_int_set (&self->processing, FALSE); + } + + GST_DEBUG_OBJECT (self, "Encoding task destroyed: %s", + gst_flow_get_name (self->output_flow)); + +} + +static gboolean +gst_v4l2_video_enc_set_crop (GstV4l2Object * obj) +{ + struct v4l2_crop crop = { 0 }; + + crop.type = obj->type; + crop.c.left = 0; + crop.c.top = 0; + crop.c.width = obj->info.width; + crop.c.height = obj->info.height; + + GST_DEBUG_OBJECT (obj->element, + "Desired cropping left %u, top %u, size %ux%u", crop.c.left, crop.c.top, + crop.c.width, crop.c.height); + + if (v4l2_ioctl (obj->video_fd, VIDIOC_S_CROP, &crop) < 0) { + GST_WARNING_OBJECT (obj->element, "VIDIOC_S_CROP failed"); + return FALSE; + } + + GST_DEBUG_OBJECT (obj->element, + "Got cropping left %u, top %u, size %ux%u", crop.c.left, crop.c.top, + crop.c.width, crop.c.height); + + return TRUE; +} + +static GstFlowReturn +gst_v4l2_video_enc_handle_frame (GstVideoEncoder * encoder, + GstVideoCodecFrame * frame, GstCaps * outcaps) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + GstFlowReturn ret = GST_FLOW_OK; + + GST_DEBUG_OBJECT (self, "Handling frame %d", frame->system_frame_number); + + if (G_UNLIKELY (!g_atomic_int_get (&self->active))) + goto flushing; + + if (NULL != outcaps) { + GstBufferPool *pool; + + /* Set capture format first */ + if(!gst_v4l2_object_set_enc_format (self->v4l2capture, outcaps, TRUE)) + goto not_negotiated; + + if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2output))) { + if (!self->input_state) + goto not_negotiated; + if (!gst_v4l2_object_set_enc_format (self->v4l2output, self->input_state->caps, TRUE)) + goto not_negotiated; + } + + gst_v4l2_video_enc_set_crop (self->v4l2output); + + pool = GST_BUFFER_POOL (self->v4l2output->pool); + + if (!gst_buffer_pool_is_active (pool)) { + GstStructure *config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (config, + self->input_state->caps, self->v4l2output->info.size, 2, 2); + + if (!gst_buffer_pool_set_config (pool, config)) + goto activate_failed; + + if (!gst_buffer_pool_set_active (pool, TRUE)) + goto activate_failed; + } + + gst_video_encoder_set_output_state (encoder, outcaps, self->input_state); + + if (!gst_video_encoder_negotiate (encoder)) { + if (GST_PAD_IS_FLUSHING (encoder->srcpad)) + goto flushing; + else + goto not_negotiated; + } + + if (!gst_buffer_pool_set_active + (GST_BUFFER_POOL (self->v4l2capture->pool), TRUE)) { + GST_DEBUG ("active capture pool failed"); + goto activate_failed; + } + } + + if (g_atomic_int_get (&self->processing) == FALSE) { + /* It possible that the processing thread stopped due to an error */ + if (self->output_flow != GST_FLOW_OK && + self->output_flow != GST_FLOW_FLUSHING) { + GST_DEBUG_OBJECT (self, "Processing loop stopped with error, leaving"); + ret = self->output_flow; + goto drop; + } + + GST_DEBUG_OBJECT (self, "Starting encoding thread"); + + /* Start the processing task, when it quits, the task will disable input + * processing to unlock input if draining, or prevent potential block */ + g_atomic_int_set (&self->processing, TRUE); + if (!gst_pad_start_task (encoder->srcpad, + (GstTaskFunction) gst_v4l2_video_enc_loop, self, + (GDestroyNotify) gst_v4l2_video_enc_loop_stopped)) + goto start_task_failed; + } + + if (frame->input_buffer) { + GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder); + ret = + gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL + (self->v4l2output->pool), &frame->input_buffer); + GST_VIDEO_ENCODER_STREAM_LOCK (encoder); + + if (ret == GST_FLOW_FLUSHING) { + if (g_atomic_int_get (&self->processing) == FALSE) + ret = self->output_flow; + goto drop; + } else if (ret != GST_FLOW_OK) { + goto process_failed; + } + + /* No need to keep input arround */ + gst_buffer_replace (&frame->input_buffer, NULL); + } + + gst_video_codec_frame_unref (frame); + return ret; + /* ERRORS */ +not_negotiated: + { + GST_ERROR_OBJECT (self, "not negotiated"); + ret = GST_FLOW_NOT_NEGOTIATED; + goto drop; + } +activate_failed: + { + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, + (_("Failed to allocate required memory.")), + ("Buffer pool activation failed")); + return GST_FLOW_ERROR; + + } +flushing: + { + ret = GST_FLOW_FLUSHING; + goto drop; + } +start_task_failed: + { + GST_ELEMENT_ERROR (self, RESOURCE, FAILED, + (_("Failed to start encoding thread.")), (NULL)); + g_atomic_int_set (&self->processing, FALSE); + ret = GST_FLOW_ERROR; + goto drop; + } +process_failed: + { + GST_ELEMENT_ERROR (self, RESOURCE, FAILED, + (_("Failed to process frame.")), + ("Maybe be due to not enough memory or failing driver")); + ret = GST_FLOW_ERROR; + goto drop; + } +drop: + { + gst_video_encoder_finish_frame (encoder, frame); + return ret; + } +} + +static gboolean +gst_v4l2_video_enc_decide_allocation (GstVideoEncoder * + encoder, GstQuery * query) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + GstClockTime latency; + gboolean ret = FALSE; + + self->v4l2capture->info.size = MAX_CODEC_FRAME; + + if (gst_v4l2_object_decide_allocation (self->v4l2capture, query)) + ret = + GST_VIDEO_ENCODER_CLASS + (parent_class)->decide_allocation (encoder, query); + latency = self->v4l2capture->min_buffers * self->v4l2capture->duration; + gst_video_encoder_set_latency (encoder, latency, latency); + return ret; +} + +static gboolean +gst_v4l2_video_enc_propose_allocation (GstVideoEncoder * + encoder, GstQuery * query) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + gboolean ret = FALSE; + + GST_DEBUG_OBJECT (self, "called"); +/* + if (query == NULL) + ret = TRUE; + else + ret = gst_v4l2_object_propose_allocation (self->v4l2output, query); +*/ + if (ret) + ret = GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder, + query); + + return ret; +} + +static gboolean +gst_v4l2_video_enc_src_query (GstVideoEncoder * encoder, GstQuery * query) +{ + gboolean ret = TRUE; + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS:{ + GstCaps *filter, *result = NULL; + GstPad *pad = GST_VIDEO_ENCODER_SRC_PAD (encoder); + + gst_query_parse_caps (query, &filter); + + if (self->probed_srccaps) + result = gst_caps_ref (self->probed_srccaps); + else + result = gst_pad_get_pad_template_caps (pad); + + if (filter) { + GstCaps *tmp = result; + result = + gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (tmp); + } + + GST_DEBUG_OBJECT (self, "Returning src caps %" GST_PTR_FORMAT, result); + + gst_query_set_caps_result (query, result); + gst_caps_unref (result); + break; + } + + default: + ret = GST_VIDEO_ENCODER_CLASS (parent_class)->src_query (encoder, query); + break; + } + + return ret; +} + +static gboolean +gst_v4l2_video_enc_sink_query (GstVideoEncoder * encoder, GstQuery * query) +{ + gboolean ret = TRUE; + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS:{ + GstCaps *filter, *result = NULL; + GstPad *pad = GST_VIDEO_ENCODER_SINK_PAD (encoder); + + gst_query_parse_caps (query, &filter); + + if (self->probed_sinkcaps) + result = gst_caps_ref (self->probed_sinkcaps); + else + result = gst_pad_get_pad_template_caps (pad); + + if (filter) { + GstCaps *tmp = result; + result = + gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (tmp); + } + + GST_DEBUG_OBJECT (self, "Returning sink caps %" GST_PTR_FORMAT, result); + + gst_query_set_caps_result (query, result); + gst_caps_unref (result); + break; + } + + default: + ret = GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (encoder, query); + break; + } + + return ret; +} + +static gboolean +gst_v4l2_video_enc_sink_event (GstVideoEncoder * encoder, GstEvent * event) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + gboolean ret; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + GST_DEBUG_OBJECT (self, "flush start"); + gst_v4l2_object_unlock (self->v4l2output); + gst_v4l2_object_unlock (self->v4l2capture); + break; + default: + break; + } + + ret = GST_VIDEO_ENCODER_CLASS (parent_class)->sink_event (encoder, event); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + gst_pad_stop_task (encoder->srcpad); + GST_DEBUG_OBJECT (self, "flush start done"); + default: + break; + } + + return ret; +} + +static GstStateChangeReturn +gst_v4l2_video_enc_change_state (GstElement * element, + GstStateChange transition) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (element); + GstVideoEncoder *encoder = GST_VIDEO_ENCODER (element); + + if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) { + g_atomic_int_set (&self->active, FALSE); + gst_v4l2_object_unlock (self->v4l2output); + gst_v4l2_object_unlock (self->v4l2capture); + gst_pad_stop_task (encoder->srcpad); + } + + return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); +} + +static void +gst_v4l2_video_enc_dispose (GObject * object) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (object); + + gst_caps_replace (&self->probed_sinkcaps, NULL); + gst_caps_replace (&self->probed_srccaps, NULL); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_v4l2_video_enc_finalize (GObject * object) +{ + GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (object); + + gst_v4l2_object_destroy (self->v4l2capture); + gst_v4l2_object_destroy (self->v4l2output); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_v4l2_video_enc_init (GstV4l2VideoEnc * self) +{ + self->v4l2output = gst_v4l2_object_new (GST_ELEMENT (self), + V4L2_BUF_TYPE_VIDEO_OUTPUT, DEFAULT_PROP_DEVICE, + gst_v4l2_get_output, gst_v4l2_set_output, NULL); + self->v4l2output->no_initial_format = TRUE; + self->v4l2output->keep_aspect = FALSE; + + self->v4l2capture = gst_v4l2_object_new (GST_ELEMENT (self), + V4L2_BUF_TYPE_VIDEO_CAPTURE, DEFAULT_PROP_DEVICE, + gst_v4l2_get_input, gst_v4l2_set_input, NULL); + self->v4l2capture->no_initial_format = TRUE; + self->v4l2output->keep_aspect = FALSE; +} + +static void +gst_v4l2_video_enc_class_init (GstV4l2VideoEncClass * klass) +{ + GstElementClass *element_class; + GObjectClass *gobject_class; + GstVideoEncoderClass *video_encoder_class; + + parent_class = g_type_class_peek_parent (klass); + + element_class = (GstElementClass *) klass; + gobject_class = (GObjectClass *) klass; + video_encoder_class = (GstVideoEncoderClass *) klass; + + GST_DEBUG_CATEGORY_INIT (gst_v4l2_video_enc_debug, "v4l2videoenc", 0, + "V4L2 Video Encoder"); + + gst_element_class_set_static_metadata (element_class, + "V4L2 Video Encoder", + "Codec/Encoder/Video", + "Encode video streams via V4L2 API", "ayaka "); + + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_v4l2_object_get_raw_caps ())); + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_v4l2_object_get_codec_caps ())); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_finalize); + gobject_class->set_property = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_set_property); + gobject_class->get_property = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_get_property); + + video_encoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_open); + video_encoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_close); + video_encoder_class->start = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_start); + video_encoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_stop); + video_encoder_class->finish = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_finish); + video_encoder_class->flush = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_flush); + video_encoder_class->set_format = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_set_format); + video_encoder_class->negotiate = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_negotiate); + video_encoder_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_decide_allocation); + video_encoder_class->propose_allocation = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_propose_allocation); + video_encoder_class->sink_query = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_sink_query); + video_encoder_class->src_query = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_src_query); + video_encoder_class->sink_event = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_sink_event); + + + klass->handle_frame = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_handle_frame); + + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_change_state); + + gst_v4l2_object_install_m2m_properties_helper (gobject_class); + + g_object_class_install_property (gobject_class, PROP_BITRATE, + g_param_spec_int ("bitrate", "bitrate", "v4l2 encoder bitrate", + 0, G_MAXINT, 400000, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PREPEND_HDR, + g_param_spec_int ("prepend-hdr", "prepend-hdr", "v4l2 encoder prepend header", + 0, 1, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_GOP, + g_param_spec_int ("gop", "gop", "v4l2 encoder gop", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); +} + +/* Probing functions */ +gboolean +gst_v4l2_is_video_enc (GstCaps * sink_caps, GstCaps * src_caps) +{ + gboolean ret = FALSE; + + if (gst_caps_is_subset (sink_caps, gst_v4l2_object_get_raw_caps ()) + && gst_caps_is_subset (src_caps, gst_v4l2_object_get_codec_caps ())) + ret = TRUE; + + return ret; +} diff --git a/sys/v4l2/gstv4l2videoenc.h b/sys/v4l2/gstv4l2videoenc.h new file mode 100755 index 0000000..0ba4740 --- /dev/null +++ b/sys/v4l2/gstv4l2videoenc.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2014 SUMOMO Computer Association. + * Author: ayaka + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GST_V4L2_VIDEO_ENC_H__ +#define __GST_V4L2_VIDEO_ENC_H__ + +#include +#include +#include +#include + +#include +#include + +GST_DEBUG_CATEGORY_EXTERN (v4l2videoenc_debug); + +G_BEGIN_DECLS +#define GST_TYPE_V4L2_VIDEO_ENC \ + (gst_v4l2_video_enc_get_type()) +#define GST_V4L2_VIDEO_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_VIDEO_ENC,GstV4l2VideoEnc)) +#define GST_V4L2_VIDEO_ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_VIDEO_ENC,GstV4l2VideoEncClass)) +#define GST_IS_V4L2_VIDEO_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_VIDEO_ENC)) +#define GST_IS_V4L2_VIDEO_ENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_VIDEO_ENC)) +typedef struct _GstV4l2VideoEnc GstV4l2VideoEnc; +typedef struct _GstV4l2VideoEncClass GstV4l2VideoEncClass; + +struct _GstV4l2VideoEnc +{ + GstVideoEncoder parent; + + /* < private > */ + GstV4l2Object *v4l2output; + GstV4l2Object *v4l2capture; + + /* pads */ + GstCaps *probed_srccaps; + GstCaps *probed_sinkcaps; + + /* State */ + GstVideoCodecState *input_state; + gboolean active; + gboolean processing; + gboolean finish; + GstFlowReturn output_flow; + + /*properity*/ + gint bitrate; + gint gop; + gint prepend_hdr; + +}; + +struct _GstV4l2VideoEncClass +{ + GstVideoEncoderClass parent_class; + + gchar *default_device; + + GstFlowReturn (*handle_frame) (GstVideoEncoder * encoder, + GstVideoCodecFrame * frame, GstCaps * outcaps); +}; + +GType gst_v4l2_video_enc_get_type (void); + +gboolean gst_v4l2_is_video_enc (GstCaps * sink_caps, GstCaps * src_caps); + +void +gst_v4l2_video_enc_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); + +void +gst_v4l2_video_enc_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +gboolean gst_v4l2_video_enc_register (GstPlugin * plugin, + const gchar *basename, + const gchar *device_path, + GstCaps * sink_caps, GstCaps * src_caps); + +G_END_DECLS +#endif /* __GST_V4L2_VIDEO_ENC_H__ */ diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c old mode 100644 new mode 100755 index bfb6557..37f7a61 --- a/sys/v4l2/v4l2_calls.c +++ b/sys/v4l2/v4l2_calls.c @@ -39,6 +39,7 @@ #include #endif #include "v4l2_calls.h" +#include "v4l2-utils.h" #include "gstv4l2tuner.h" #if 0 #include "gstv4l2xoverlay.h" @@ -514,6 +515,11 @@ gst_v4l2_open (GstV4l2Object * v4l2object) { struct stat st; int libv4l2_fd; + GstV4l2Iterator *it; + gboolean ret = TRUE; + it = gst_v4l2_iterator_new (); + +retry: GST_DEBUG_OBJECT (v4l2object->element, "Trying to open device %s", v4l2object->videodev); @@ -555,10 +561,31 @@ gst_v4l2_open (GstV4l2Object * v4l2object) goto error; /* do we need to be a capture device? */ + /* if (GST_IS_V4L2SRC (v4l2object->element) && !(v4l2object->vcap.capabilities & (V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VIDEO_CAPTURE_MPLANE))) - goto not_capture; + V4L2_CAP_VIDEO_CAPTURE_MPLANE)))*/ + /* marked for mtk video decode will set V4L2_CAP_VIDEO_CAPTURE and + V4L2_CAP_VIDEO_CAPTURE_MPLANE flag*/ + + if (GST_IS_V4L2SRC (v4l2object->element) && + !(v4l2object->vcap.capabilities & (V4L2_CAP_VIDEO_CAPTURE))) { + ret = gst_v4l2_iterator_next (it); + if (ret) { + v4l2object->videodev = it->device_path; + if (GST_V4L2_IS_OPEN (v4l2object)) { + v4l2_close (v4l2object->video_fd); + v4l2object->video_fd = -1; + } + goto retry; + } + else { + GST_DEBUG_OBJECT (v4l2object->element, "Cannot find capure device"); + gst_v4l2_iterator_free (it); + goto not_capture; + } + } + gst_v4l2_iterator_free (it); if (GST_IS_V4L2SINK (v4l2object->element) && !(v4l2object->vcap.capabilities & (V4L2_CAP_VIDEO_OUTPUT | -- 1.9.1