X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=sys%2Fv4l2%2Fgstv4l2videodec.c;h=f1c32e81e9889051886b321efb390551d1117603;hb=63f9f02c627a2bf40051222e0238deda912e68de;hp=50a51104f60421174e97514e3fc1b1e6e1472307;hpb=ab02b1db6c436c9b08a6ab2e48214eb89d126e17;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/sys/v4l2/gstv4l2videodec.c b/sys/v4l2/gstv4l2videodec.c index 50a5110..f1c32e8 100644 --- a/sys/v4l2/gstv4l2videodec.c +++ b/sys/v4l2/gstv4l2videodec.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2014 Collabora Ltd. - * Author: Nicolas Dufresne + * Author: Nicolas Dufresne * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -29,70 +29,56 @@ #include #include +#include "gstv4l2object.h" #include "gstv4l2videodec.h" -#include "v4l2_calls.h" #include #include -#define DEFAULT_PROP_DEVICE "/dev/video0" - -#define V4L2_VIDEO_DEC_QUARK \ - g_quark_from_static_string("gst-v4l2-video-dec-info") - GST_DEBUG_CATEGORY_STATIC (gst_v4l2_video_dec_debug); #define GST_CAT_DEFAULT gst_v4l2_video_dec_debug -static gboolean gst_v4l2_video_dec_flush (GstVideoDecoder * decoder); - typedef struct { gchar *device; GstCaps *sink_caps; GstCaps *src_caps; -} Gstv4l2VideoDecQData; + const gchar *longname; + const gchar *description; +} GstV4l2VideoDecCData; enum { PROP_0, - V4L2_STD_OBJECT_PROPS, - PROP_CAPTURE_IO_MODE, + V4L2_STD_OBJECT_PROPS +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + , PROP_TBM_OUTPUT +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ }; -static void gst_v4l2_video_dec_class_init (GstV4l2VideoDecClass * klass); -static void gst_v4l2_video_dec_init (GstV4l2VideoDec * self, gpointer g_class); -static void gst_v4l2_video_dec_base_init (gpointer g_class); +#define gst_v4l2_video_dec_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE (GstV4l2VideoDec, gst_v4l2_video_dec, + GST_TYPE_VIDEO_DECODER); -static GstVideoDecoderClass *parent_class = NULL; +static GstFlowReturn gst_v4l2_video_dec_finish (GstVideoDecoder * decoder); -GType -gst_v4l2_video_dec_get_type (void) +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER +static void gst_v4l2_video_dec_flush_buffer_event (GstVideoDecoder * decoder) { - static volatile gsize type = 0; + gboolean ret = FALSE; - if (g_once_init_enter (&type)) { - GType _type; - static const GTypeInfo info = { - sizeof (GstV4l2VideoDecClass), - gst_v4l2_video_dec_base_init, - NULL, - (GClassInitFunc) gst_v4l2_video_dec_class_init, - NULL, - NULL, - sizeof (GstV4l2VideoDec), - 0, - (GInstanceInitFunc) gst_v4l2_video_dec_init, - NULL - }; + if (!decoder) { + GST_ERROR("no decoder"); + return; + } - _type = g_type_register_static (GST_TYPE_VIDEO_DECODER, "GstV4l2VideoDec", - &info, 0); + ret = gst_pad_push_event (decoder->srcpad, + gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM | GST_EVENT_TYPE_SERIALIZED, + gst_structure_new_empty("tizen/flush-buffer"))); - g_once_init_leave (&type, _type); - } - return type; + GST_WARNING_OBJECT(decoder, "event push ret[%d] for flush-buffer", ret); } - +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ static void gst_v4l2_video_dec_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -100,23 +86,19 @@ gst_v4l2_video_dec_set_property (GObject * object, GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (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_id, value, - pspec); - break; case PROP_CAPTURE_IO_MODE: - gst_v4l2_object_set_property_helper (self->v4l2capture, prop_id, value, - pspec); + if (!gst_v4l2_object_set_property_helper (self->v4l2capture, + prop_id, value, pspec)) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, 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); +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + case PROP_TBM_OUTPUT: + self->v4l2capture->tbm_output = g_value_get_boolean (value); + GST_INFO_OBJECT (self, "tbm output [%d]", self->v4l2capture->tbm_output); break; +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ + /* By default, only set on output */ default: @@ -135,14 +117,18 @@ gst_v4l2_video_dec_get_property (GObject * object, GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (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); + if (!gst_v4l2_object_get_property_helper (self->v4l2capture, + prop_id, value, pspec)) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } break; +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + case PROP_TBM_OUTPUT: + GST_INFO_OBJECT (self, "tbm output [%d]", self->v4l2capture->tbm_output); + g_value_set_boolean (value, self->v4l2capture->tbm_output); + break; +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ /* By default read from output */ default: @@ -158,6 +144,7 @@ static gboolean gst_v4l2_video_dec_open (GstVideoDecoder * decoder) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); + GstCaps *codec_caps; GST_DEBUG_OBJECT (self, "Opening"); @@ -167,30 +154,19 @@ gst_v4l2_video_dec_open (GstVideoDecoder * decoder) 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_codec_caps ()); + codec_caps = gst_pad_get_pad_template_caps (decoder->sinkpad); + self->probed_sinkcaps = gst_v4l2_object_probe_caps (self->v4l2output, + codec_caps); + gst_caps_unref (codec_caps); if (gst_caps_is_empty (self->probed_sinkcaps)) goto no_encoded_format; - self->probed_srccaps = gst_v4l2_object_get_caps (self->v4l2capture, - gst_v4l2_object_get_raw_caps ()); - - if (gst_caps_is_empty (self->probed_srccaps)) - goto no_raw_format; - return TRUE; no_encoded_format: GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, - (_("Encoder on device %s has no supported input format"), - self->v4l2output->videodev), (NULL)); - goto failure; - - -no_raw_format: - GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, - (_("Encoder on device %s has no supported output format"), + (_("Decoder on device %s has no supported input format"), self->v4l2output->videodev), (NULL)); goto failure; @@ -243,9 +219,21 @@ gst_v4l2_video_dec_stop (GstVideoDecoder * decoder) 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 (decoder->srcpad); + + GST_VIDEO_DECODER_STREAM_LOCK (decoder); + self->output_flow = GST_FLOW_OK; + GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); + /* Should have been flushed already */ g_assert (g_atomic_int_get (&self->active) == FALSE); - g_assert (g_atomic_int_get (&self->processing) == FALSE); +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + gst_v4l2_video_dec_flush_buffer_event (decoder); +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ gst_v4l2_object_stop (self->v4l2output); gst_v4l2_object_stop (self->v4l2capture); @@ -264,6 +252,7 @@ static gboolean gst_v4l2_video_dec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state) { + GstV4l2Error error = GST_V4L2_ERROR_INIT; gboolean ret = TRUE; GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); @@ -275,17 +264,61 @@ gst_v4l2_video_dec_set_format (GstVideoDecoder * decoder, goto done; } gst_video_codec_state_unref (self->input_state); + self->input_state = NULL; + + gst_v4l2_video_dec_finish (decoder); + gst_v4l2_object_stop (self->v4l2output); + + /* The renegotiation flow don't blend with the base class flow. To properly + * stop the capture pool, if the buffers can't be orphaned, we need to + * reclaim our buffers, which will happend through the allocation query. + * The allocation query is triggered by gst_video_decoder_negotiate() which + * requires the output caps to be set, but we can't know this information + * as we rely on the decoder, which requires the capture queue to be + * stopped. + * + * To workaround this issue, we simply run an allocation query with the + * old negotiated caps in order to drain/reclaim our buffers. That breaks + * the complexity and should not have much impact in performance since the + * following allocation query will happen on a drained pipeline and won't + * block. */ + if (self->v4l2capture->pool && + !gst_v4l2_buffer_pool_orphan (&self->v4l2capture->pool)) { + GstCaps *caps = gst_pad_get_current_caps (decoder->srcpad); + if (caps) { + GstQuery *query = gst_query_new_allocation (caps, FALSE); + gst_pad_peer_query (decoder->srcpad, query); + gst_query_unref (query); + gst_caps_unref (caps); + } + } - /* FIXME we probably need to do more work if pools are active */ + gst_v4l2_object_stop (self->v4l2capture); + self->output_flow = GST_FLOW_OK; } - ret = gst_v4l2_object_set_format (self->v4l2output, state->caps); + ret = gst_v4l2_object_set_format (self->v4l2output, state->caps, &error); + + gst_caps_replace (&self->probed_srccaps, NULL); + self->probed_srccaps = gst_v4l2_object_probe_caps (self->v4l2capture, + gst_v4l2_object_get_raw_caps ()); + + if (gst_caps_is_empty (self->probed_srccaps)) + goto no_raw_format; if (ret) self->input_state = gst_video_codec_state_ref (state); + else + gst_v4l2_error (self, &error); done: return ret; + +no_raw_format: + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, + (_("Decoder on device %s has no supported output format"), + self->v4l2output->videodev), (NULL)); + return GST_FLOW_ERROR; } static gboolean @@ -293,27 +326,83 @@ gst_v4l2_video_dec_flush (GstVideoDecoder * decoder) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); - GST_DEBUG_OBJECT (self, "Flushing"); + GST_DEBUG_OBJECT (self, "Flushed"); - /* Wait for capture thread to stop */ - gst_pad_stop_task (decoder->srcpad); - self->output_flow = GST_FLOW_OK; + /* Ensure the processing thread has stopped for the reverse playback + * discount case */ + if (gst_pad_get_task_state (decoder->srcpad) == GST_TASK_STARTED) { + GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); - gst_v4l2_buffer_pool_flush (GST_V4L2_BUFFER_POOL (self->v4l2output->pool)); - gst_v4l2_buffer_pool_flush (GST_V4L2_BUFFER_POOL (self->v4l2capture->pool)); + gst_v4l2_object_unlock (self->v4l2output); + gst_v4l2_object_unlock (self->v4l2capture); + gst_pad_stop_task (decoder->srcpad); + GST_VIDEO_DECODER_STREAM_LOCK (decoder); + } - /* Output will remain flushing until new frame comes in */ + self->output_flow = GST_FLOW_OK; + + gst_v4l2_object_unlock_stop (self->v4l2output); gst_v4l2_object_unlock_stop (self->v4l2capture); + if (self->v4l2output->pool) + gst_v4l2_buffer_pool_flush (self->v4l2output->pool); + +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + gst_v4l2_video_dec_flush_buffer_event (decoder); +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ + /* gst_v4l2_buffer_pool_flush() calls streamon the capture pool and must be + * called after gst_v4l2_object_unlock_stop() stopped flushing the buffer + * pool. */ + if (self->v4l2capture->pool) + gst_v4l2_buffer_pool_flush (self->v4l2capture->pool); + return TRUE; } static gboolean gst_v4l2_video_dec_negotiate (GstVideoDecoder * decoder) { + GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); + + /* We don't allow renegotiation without carefull disabling the pool */ + if (self->v4l2capture->pool && + gst_buffer_pool_is_active (GST_BUFFER_POOL (self->v4l2capture->pool))) + return TRUE; + return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); } +static gboolean +gst_v4l2_decoder_cmd (GstV4l2Object * v4l2object, guint cmd, guint flags) +{ + struct v4l2_decoder_cmd dcmd = { 0, }; + + GST_DEBUG_OBJECT (v4l2object->element, + "sending v4l2 decoder command %u with flags %u", cmd, flags); + + if (!GST_V4L2_IS_OPEN (v4l2object)) + return FALSE; + + dcmd.cmd = cmd; + dcmd.flags = flags; + if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_DECODER_CMD, &dcmd) < 0) + goto dcmd_failed; + + return TRUE; + +dcmd_failed: + if (errno == ENOTTY) { + GST_INFO_OBJECT (v4l2object->element, + "Failed to send decoder command %u with flags %u for '%s'. (%s)", + cmd, flags, v4l2object->videodev, g_strerror (errno)); + } else { + GST_ERROR_OBJECT (v4l2object->element, + "Failed to send decoder command %u with flags %u for '%s'. (%s)", + cmd, flags, v4l2object->videodev, g_strerror (errno)); + } + return FALSE; +} + static GstFlowReturn gst_v4l2_video_dec_finish (GstVideoDecoder * decoder) { @@ -321,34 +410,65 @@ gst_v4l2_video_dec_finish (GstVideoDecoder * decoder) GstFlowReturn ret = GST_FLOW_OK; GstBuffer *buffer; - if (!self->input_state) + if (gst_pad_get_task_state (decoder->srcpad) != GST_TASK_STARTED) goto done; GST_DEBUG_OBJECT (self, "Finishing decoding"); - /* Keep queuing empty buffers until the processing thread has stopped, - * _pool_process() will return FLUSHING when that happened */ GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); - 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); + + if (gst_v4l2_decoder_cmd (self->v4l2output, V4L2_DEC_CMD_STOP, 0)) { + GstTask *task = decoder->srcpad->task; + + /* If the decoder stop command succeeded, just wait until processing is + * finished */ + GST_DEBUG_OBJECT (self, "Waiting for decoder stop"); + GST_OBJECT_LOCK (task); + while (GST_TASK_STATE (task) == GST_TASK_STARTED) + GST_TASK_WAIT (task); + GST_OBJECT_UNLOCK (task); + ret = GST_FLOW_FLUSHING; + } else { + /* otherwise keep queuing empty buffers until the processing thread has + * stopped, _pool_process() will return FLUSHING when that happened */ + 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); + } } - GST_VIDEO_DECODER_STREAM_LOCK (decoder); - g_assert (g_atomic_int_get (&self->processing) == FALSE); + /* and ensure the processing thread has stopped in case another error + * occured. */ + gst_v4l2_object_unlock (self->v4l2capture); + gst_pad_stop_task (decoder->srcpad); + GST_VIDEO_DECODER_STREAM_LOCK (decoder); if (ret == GST_FLOW_FLUSHING) ret = self->output_flow; GST_DEBUG_OBJECT (decoder, "Done draining buffers"); + /* TODO Shall we cleanup any reffed frame to workaround broken decoders ? */ + done: return ret; } +static GstFlowReturn +gst_v4l2_video_dec_drain (GstVideoDecoder * decoder) +{ + GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); + + GST_DEBUG_OBJECT (self, "Draining..."); + gst_v4l2_video_dec_finish (decoder); + gst_v4l2_video_dec_flush (decoder); + + return GST_FLOW_OK; +} + static GstVideoCodecFrame * gst_v4l2_video_dec_get_oldest_frame (GstVideoDecoder * decoder) { @@ -383,6 +503,7 @@ static void gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); + GstV4l2BufferPool *v4l2_pool = GST_V4L2_BUFFER_POOL (self->v4l2capture->pool); GstBufferPool *pool; GstVideoCodecFrame *frame; GstBuffer *buffer = NULL; @@ -390,25 +511,30 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) GST_LOG_OBJECT (decoder, "Allocate output buffer"); - /* We cannot use the base class allotate helper since it taking the internal - * stream lock. we know that the acquire may need to poll until more frames - * comes in and holding this lock would prevent that. - */ - pool = gst_video_decoder_get_buffer_pool (decoder); - ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL); - g_object_unref (pool); + self->output_flow = GST_FLOW_OK; + do { + /* We cannot use the base class allotate helper since it taking the internal + * stream lock. we know that the acquire may need to poll until more frames + * comes in and holding this lock would prevent that. + */ + pool = gst_video_decoder_get_buffer_pool (decoder); - if (ret != GST_FLOW_OK) - goto beach; + /* Pool may be NULL if we started going to READY state */ + if (pool == NULL) { + ret = GST_FLOW_FLUSHING; + goto beach; + } - /* Check if buffer isn't the last one */ - if (gst_buffer_get_size (buffer) == 0) - goto beach; + ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL); + g_object_unref (pool); + + if (ret != GST_FLOW_OK) + goto beach; - GST_LOG_OBJECT (decoder, "Process output buffer"); - ret = - gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self-> - v4l2capture->pool), buffer); + GST_LOG_OBJECT (decoder, "Process output buffer"); + ret = gst_v4l2_buffer_pool_process (v4l2_pool, &buffer); + + } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER); if (ret != GST_FLOW_OK) goto beach; @@ -430,21 +556,65 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder) return; beach: - GST_DEBUG_OBJECT (decoder, "Leaving output thread"); + GST_DEBUG_OBJECT (decoder, "Leaving output thread: %s", + gst_flow_get_name (ret)); 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 (decoder->srcpad); } +static gboolean +gst_v4l2_video_remove_padding (GstCapsFeatures * features, + GstStructure * structure, gpointer user_data) +{ + GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (user_data); + GstVideoAlignment *align = &self->v4l2capture->align; + GstVideoInfo *info = &self->v4l2capture->info; + int width, height; + + if (!gst_structure_get_int (structure, "width", &width)) + return TRUE; + + if (!gst_structure_get_int (structure, "height", &height)) + return TRUE; + + if (align->padding_left != 0 || align->padding_top != 0 || + height != info->height + align->padding_bottom) + return TRUE; + + if (height == info->height + align->padding_bottom) { + /* Some drivers may round up width to the padded with */ + if (width == info->width + align->padding_right) + gst_structure_set (structure, + "width", G_TYPE_INT, width - align->padding_right, + "height", G_TYPE_INT, height - align->padding_bottom, NULL); + /* Some drivers may keep visible width and only round up bytesperline */ + else if (width == info->width) + gst_structure_set (structure, + "height", G_TYPE_INT, height - align->padding_bottom, NULL); + } + + return TRUE; +} + static GstFlowReturn gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) { + GstV4l2Error error = GST_V4L2_ERROR_INIT; GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); GstFlowReturn ret = GST_FLOW_OK; + gboolean processed = FALSE; + GstBuffer *tmp; + GstTaskState task_state; +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + GstStructure *structure = NULL; + const gchar *caps_format = NULL; + GstMessage *msg = NULL; + GstV4l2BufferPool *capture_pool = NULL; +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ GST_DEBUG_OBJECT (self, "Handling frame %d", frame->system_frame_number); @@ -452,14 +622,20 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, goto flushing; if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2output))) { - if (!gst_v4l2_object_set_format (self->v4l2output, self->input_state->caps)) + if (!self->input_state) + goto not_negotiated; + if (!gst_v4l2_object_set_format (self->v4l2output, self->input_state->caps, + &error)) goto not_negotiated; } if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) { + GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool); GstVideoInfo info; GstVideoCodecState *output_state; GstBuffer *codec_data; + GstCaps *acquired_caps, *available_caps, *caps, *filter; + GstStructure *st; GST_DEBUG_OBJECT (self, "Sending header"); @@ -472,22 +648,92 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, if (codec_data) { gst_buffer_ref (codec_data); } else { - codec_data = frame->input_buffer; - frame->input_buffer = NULL; + codec_data = gst_buffer_ref (frame->input_buffer); + processed = TRUE; + } + + /* Ensure input internal pool is active */ + 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); + + /* There is no reason to refuse this config */ + if (!gst_buffer_pool_set_config (pool, config)) + goto activate_failed; + + if (!gst_buffer_pool_set_active (pool, TRUE)) + goto activate_failed; } GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); - gst_v4l2_object_unlock_stop (self->v4l2output); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self-> - v4l2output->pool), codec_data); - gst_v4l2_object_unlock (self->v4l2output); + v4l2output->pool), &codec_data); GST_VIDEO_DECODER_STREAM_LOCK (decoder); gst_buffer_unref (codec_data); - if (!gst_v4l2_object_setup_format (self->v4l2capture, &info, &self->align)) + /* For decoders G_FMT returns coded size, G_SELECTION returns visible size + * in the compose rectangle. gst_v4l2_object_acquire_format() checks both + * and returns the visible size as with/height and the coded size as + * padding. */ + if (!gst_v4l2_object_acquire_format (self->v4l2capture, &info)) + goto not_negotiated; + + /* Create caps from the acquired format, remove the format field */ + acquired_caps = gst_video_info_to_caps (&info); + GST_DEBUG_OBJECT (self, "Acquired caps: %" GST_PTR_FORMAT, acquired_caps); + st = gst_caps_get_structure (acquired_caps, 0); + gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site", + NULL); + + /* Probe currently available pixel formats */ + available_caps = gst_caps_copy (self->probed_srccaps); + GST_DEBUG_OBJECT (self, "Available caps: %" GST_PTR_FORMAT, available_caps); + + /* Replace coded size with visible size, we want to negotiate visible size + * with downstream, not coded size. */ + gst_caps_map_in_place (available_caps, gst_v4l2_video_remove_padding, self); + + filter = gst_caps_intersect_full (available_caps, acquired_caps, + GST_CAPS_INTERSECT_FIRST); + GST_DEBUG_OBJECT (self, "Filtered caps: %" GST_PTR_FORMAT, filter); + gst_caps_unref (acquired_caps); + gst_caps_unref (available_caps); + caps = gst_pad_peer_query_caps (decoder->srcpad, filter); + gst_caps_unref (filter); + + GST_DEBUG_OBJECT (self, "Possible decoded caps: %" GST_PTR_FORMAT, caps); + if (gst_caps_is_empty (caps)) { + gst_caps_unref (caps); goto not_negotiated; + } + + /* Fixate pixel format */ + caps = gst_caps_fixate (caps); + + GST_DEBUG_OBJECT (self, "Chosen decoded caps: %" GST_PTR_FORMAT, caps); +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + structure = gst_caps_get_structure (caps, 0); + caps_format = gst_structure_get_string (structure, "format"); + + if (!strcmp (caps_format, "I420")) { + GST_INFO_OBJECT (self, "I420 -> S420"); + gst_caps_set_simple (caps, "format", G_TYPE_STRING, "S420", NULL); + } else if (!strcmp (caps_format, "NV12")) { + GST_INFO_OBJECT (self, "NV12 -> SN12"); + gst_caps_set_simple (caps, "format", G_TYPE_STRING, "SN12", NULL); + } + GST_INFO_OBJECT (self, "Updated decoded caps: %" GST_PTR_FORMAT, caps); +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ + + /* Try to set negotiated format, on success replace acquired format */ + if (gst_v4l2_object_set_format (self->v4l2capture, caps, &error)) + gst_video_info_from_caps (&info, caps); + else + gst_v4l2_clear_error (&error); + gst_caps_unref (caps); output_state = gst_video_decoder_set_output_state (decoder, info.finfo->format, info.width, info.height, self->input_state); @@ -502,11 +748,30 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, else goto not_negotiated; } + + /* Ensure our internal pool is activated */ + if (!gst_buffer_pool_set_active (GST_BUFFER_POOL (self->v4l2capture->pool), + TRUE)) + goto activate_failed; +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + capture_pool = GST_V4L2_BUFFER_POOL (self->v4l2capture->pool); + + msg = gst_message_new_element (GST_OBJECT_CAST (decoder), + gst_structure_new ("prepare-decode-buffers", + "num_buffers", G_TYPE_INT, capture_pool->num_allocated, + "extra_num_buffers", G_TYPE_INT, capture_pool->num_allocated - 2, NULL)); + + gst_element_post_message (GST_ELEMENT_CAST (decoder), msg); + + GST_WARNING_OBJECT (self, "output buffer[%d]", capture_pool->num_allocated); +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ } - 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) { + task_state = gst_pad_get_task_state (GST_VIDEO_DECODER_SRC_PAD (self)); + if (task_state == GST_TASK_STOPPED || task_state == GST_TASK_PAUSED) { + /* It's 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; @@ -514,32 +779,39 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, GST_DEBUG_OBJECT (self, "Starting decoding thread"); - /* Enable processing input */ - gst_v4l2_object_unlock_stop (self->v4l2output); - /* 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); - gst_pad_start_task (decoder->srcpad, - (GstTaskFunction) gst_v4l2_video_dec_loop, self, NULL); + self->output_flow = GST_FLOW_FLUSHING; + if (!gst_pad_start_task (decoder->srcpad, + (GstTaskFunction) gst_v4l2_video_dec_loop, self, NULL)) + goto start_task_failed; } - if (frame->input_buffer) { + if (!processed) { GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self->v4l2output-> - pool), frame->input_buffer); + pool), &frame->input_buffer); GST_VIDEO_DECODER_STREAM_LOCK (decoder); if (ret == GST_FLOW_FLUSHING) { - if (g_atomic_int_get (&self->processing) == FALSE) + if (gst_pad_get_task_state (GST_VIDEO_DECODER_SRC_PAD (self)) != + GST_TASK_STARTED) 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); } + /* No need to keep input arround */ + tmp = frame->input_buffer; + frame->input_buffer = gst_buffer_new (); + gst_buffer_copy_into (frame->input_buffer, tmp, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | + GST_BUFFER_COPY_META, 0, 0); + gst_buffer_unref (tmp); + gst_video_codec_frame_unref (frame); return ret; @@ -548,6 +820,15 @@ not_negotiated: { GST_ERROR_OBJECT (self, "not negotiated"); ret = GST_FLOW_NOT_NEGOTIATED; + gst_v4l2_error (self, &error); + goto drop; + } +activate_failed: + { + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, + (_("Failed to allocate required memory.")), + ("Buffer pool activation failed")); + ret = GST_FLOW_ERROR; goto drop; } flushing: @@ -555,6 +836,22 @@ flushing: ret = GST_FLOW_FLUSHING; goto drop; } + +start_task_failed: + { + GST_ELEMENT_ERROR (self, RESOURCE, FAILED, + (_("Failed to start decoding thread.")), (NULL)); + 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_decoder_drop_frame (decoder, frame); @@ -574,9 +871,15 @@ gst_v4l2_video_dec_decide_allocation (GstVideoDecoder * decoder, ret = GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (decoder, query); - latency = self->v4l2capture->min_buffers_for_capture * - self->v4l2capture->duration; - gst_video_decoder_set_latency (decoder, latency, latency); + if (GST_CLOCK_TIME_IS_VALID (self->v4l2capture->duration)) { + latency = self->v4l2capture->min_buffers * self->v4l2capture->duration; + GST_DEBUG_OBJECT (self, "Setting latency: %" GST_TIME_FORMAT " (%" + G_GUINT32_FORMAT " * %" G_GUINT64_FORMAT, GST_TIME_ARGS (latency), + self->v4l2capture->min_buffers, self->v4l2capture->duration); + gst_video_decoder_set_latency (decoder, latency, latency); + } else { + GST_WARNING_OBJECT (self, "Duration invalid, not setting latency"); + } return ret; } @@ -590,12 +893,14 @@ gst_v4l2_video_dec_src_query (GstVideoDecoder * decoder, GstQuery * query) switch (GST_QUERY_TYPE (query)) { case GST_QUERY_CAPS:{ GstCaps *filter, *result = NULL; + GstPad *pad = GST_VIDEO_DECODER_SRC_PAD (decoder); + gst_query_parse_caps (query, &filter); if (self->probed_srccaps) result = gst_caps_ref (self->probed_srccaps); else - result = gst_v4l2_object_get_raw_caps (); + result = gst_pad_get_pad_template_caps (pad); if (filter) { GstCaps *tmp = result; @@ -619,58 +924,50 @@ gst_v4l2_video_dec_src_query (GstVideoDecoder * decoder, GstQuery * query) return ret; } -static gboolean -gst_v4l2_video_dec_sink_query (GstVideoDecoder * decoder, GstQuery * query) +static GstCaps * +gst_v4l2_video_dec_sink_getcaps (GstVideoDecoder * decoder, GstCaps * filter) { - gboolean ret = TRUE; GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); + GstCaps *result; - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CAPS:{ - GstCaps *filter, *result = NULL; - gst_query_parse_caps (query, &filter); - - if (self->probed_sinkcaps) - result = gst_caps_ref (self->probed_sinkcaps); - else - result = gst_v4l2_object_get_codec_caps (); - - 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); + result = gst_video_decoder_proxy_getcaps (decoder, self->probed_sinkcaps, + filter); - gst_query_set_caps_result (query, result); - gst_caps_unref (result); - break; - } + GST_DEBUG_OBJECT (self, "Returning sink caps %" GST_PTR_FORMAT, result); - default: - ret = GST_VIDEO_DECODER_CLASS (parent_class)->sink_query (decoder, query); - break; - } - - return ret; + return result; } static gboolean gst_v4l2_video_dec_sink_event (GstVideoDecoder * decoder, GstEvent * event) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); + gboolean ret; + GstEventType type = GST_EVENT_TYPE (event); - switch (GST_EVENT_TYPE (event)) { + switch (type) { 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_DECODER_CLASS (parent_class)->sink_event (decoder, event); + + switch (type) { + case GST_EVENT_FLUSH_START: + /* The processing thread should stop now, wait for it */ + gst_pad_stop_task (decoder->srcpad); + GST_DEBUG_OBJECT (self, "flush start done"); + break; default: break; } - return GST_VIDEO_DECODER_CLASS (parent_class)->sink_event (decoder, event); + return ret; } static GstStateChangeReturn @@ -678,11 +975,13 @@ gst_v4l2_video_dec_change_state (GstElement * element, GstStateChange transition) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (element); + GstVideoDecoder *decoder = GST_VIDEO_DECODER (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 (decoder->srcpad); } return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); @@ -711,52 +1010,34 @@ gst_v4l2_video_dec_finalize (GObject * object) } static void -gst_v4l2_video_dec_base_init (gpointer g_class) +gst_v4l2_video_dec_init (GstV4l2VideoDec * self) { - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - Gstv4l2VideoDecQData *qdata; - GstPadTemplate *templ; - - qdata = g_type_get_qdata (G_TYPE_FROM_CLASS (g_class), V4L2_VIDEO_DEC_QUARK); - if (!qdata) - return; - - templ = - gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - qdata->sink_caps); - gst_element_class_add_pad_template (element_class, templ); - - templ = - gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - qdata->src_caps); - gst_element_class_add_pad_template (element_class, templ); + /* V4L2 object are created in subinstance_init */ } static void -gst_v4l2_video_dec_init (GstV4l2VideoDec * self, gpointer g_class) +gst_v4l2_video_dec_subinstance_init (GTypeInstance * instance, gpointer g_class) { - GstVideoDecoder *decoder = (GstVideoDecoder *) self; - Gstv4l2VideoDecQData *qdata; - - qdata = g_type_get_qdata (G_TYPE_FROM_CLASS (g_class), V4L2_VIDEO_DEC_QUARK); - if (!qdata) - return; + 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, qdata->device, + GST_OBJECT (GST_VIDEO_DECODER_SINK_PAD (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, qdata->device, + GST_OBJECT (GST_VIDEO_DECODER_SRC_PAD (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; - - g_object_set (self, "device", qdata->device, NULL); +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + self->v4l2capture->tbm_output = TRUE; +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ } static void @@ -772,11 +1053,8 @@ gst_v4l2_video_dec_class_init (GstV4l2VideoDecClass * klass) gobject_class = (GObjectClass *) klass; video_decoder_class = (GstVideoDecoderClass *) klass; - gst_element_class_set_static_metadata (element_class, - "V4L2 Video Decoder", - "Codec/Decoder/Video", - "Decode video streams via V4L2 API", - "Nicolas Dufresne "); + GST_DEBUG_CATEGORY_INIT (gst_v4l2_video_dec_debug, "v4l2videodec", 0, + "V4L2 Video Decoder"); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_dispose); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_finalize); @@ -791,6 +1069,7 @@ gst_v4l2_video_dec_class_init (GstV4l2VideoDecClass * klass) video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_stop); video_decoder_class->finish = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_finish); video_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_flush); + video_decoder_class->drain = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_drain); video_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_set_format); video_decoder_class->negotiate = @@ -800,8 +1079,8 @@ gst_v4l2_video_dec_class_init (GstV4l2VideoDecClass * klass) /* FIXME propose_allocation or not ? */ video_decoder_class->handle_frame = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_handle_frame); - video_decoder_class->sink_query = - GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_sink_query); + video_decoder_class->getcaps = + GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_sink_getcaps); video_decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_src_query); video_decoder_class->sink_event = @@ -810,136 +1089,174 @@ gst_v4l2_video_dec_class_init (GstV4l2VideoDecClass * klass) element_class->change_state = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_change_state); - gst_v4l2_object_install_properties_helper (gobject_class, - DEFAULT_PROP_DEVICE); - - /** - * GstV4l2VideoDec:capture-io-mode - * - * Capture IO Mode - */ - g_object_class_install_property (gobject_class, PROP_IO_MODE, - g_param_spec_enum ("capture-io-mode", "Capture IO mode", - "Capture I/O mode", - GST_TYPE_V4L2_IO_MODE, GST_V4L2_IO_AUTO, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_v4l2_object_install_m2m_properties_helper (gobject_class); +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + g_object_class_install_property (gobject_class, PROP_TBM_OUTPUT, + g_param_spec_boolean ("tbm-output", "Enable TBM for output buffer", + "It works for only DMABUF mode.", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ } -/* Probing functions */ -static GstCaps * -gst_v4l2_video_dec_probe_caps (gchar * device, gint video_fd, - enum v4l2_buf_type type, GstCaps * filter) +static void +gst_v4l2_video_dec_subclass_init (gpointer g_class, gpointer data) { - gint n; - struct v4l2_fmtdesc format; - GstCaps *ret, *caps; - - GST_DEBUG ("Getting %s format enumerations", device); - caps = gst_caps_new_empty (); - - for (n = 0;; n++) { - GstStructure *template; - - memset (&format, 0, sizeof (format)); - - format.index = n; - format.type = type; + GstV4l2VideoDecClass *klass = GST_V4L2_VIDEO_DEC_CLASS (g_class); + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstV4l2VideoDecCData *cdata = data; - if (v4l2_ioctl (video_fd, VIDIOC_ENUM_FMT, &format) < 0) - break; /* end of enumeration */ + klass->default_device = cdata->device; - GST_LOG ("index: %u", format.index); - GST_LOG ("type: %d", format.type); - GST_LOG ("flags: %08x", format.flags); - GST_LOG ("description: '%s'", format.description); - GST_LOG ("pixelformat: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (format.pixelformat)); + /* 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)); - template = gst_v4l2_object_v4l2fourcc_to_structure (format.pixelformat); + gst_element_class_set_static_metadata (element_class, cdata->longname, + "Codec/Decoder/Video/Hardware", cdata->description, + "Nicolas Dufresne "); - if (template) - gst_caps_append_structure (caps, template); - } + gst_caps_unref (cdata->sink_caps); + gst_caps_unref (cdata->src_caps); + g_free (cdata); +} - caps = gst_caps_simplify (caps); +/* Probing functions */ +gboolean +gst_v4l2_is_video_dec (GstCaps * sink_caps, GstCaps * src_caps) +{ + gboolean ret = FALSE; - ret = gst_caps_intersect (filter, caps); - gst_caps_unref (filter); - gst_caps_unref (caps); + 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_video_dec_register (GstPlugin * plugin) +static gchar * +gst_v4l2_video_dec_set_metadata (GstStructure * s, GstV4l2VideoDecCData * cdata, + const gchar * basename) { - gint i = -1; - gchar *device = NULL; - - GST_DEBUG_CATEGORY_INIT (gst_v4l2_video_dec_debug, "v4l2videodec", 0, - "V4L2 Video Decoder"); + gchar *codec_name = NULL; + gchar *type_name = NULL; + +#define SET_META(codec) \ +G_STMT_START { \ + cdata->longname = "V4L2 " codec " Decoder"; \ + cdata->description = "Decodes " codec " streams via V4L2 API"; \ + codec_name = g_ascii_strdown (codec, -1); \ +} G_STMT_END + + if (gst_structure_has_name (s, "image/jpeg")) { + SET_META ("JPEG"); + } else if (gst_structure_has_name (s, "video/mpeg")) { + gint mpegversion = 0; + gst_structure_get_int (s, "mpegversion", &mpegversion); + + if (mpegversion == 2) { + SET_META ("MPEG2"); + } else { + SET_META ("MPEG4"); + } + } else if (gst_structure_has_name (s, "video/x-h263")) { + SET_META ("H263"); + } else if (gst_structure_has_name (s, "video/x-fwht")) { + SET_META ("FWHT"); + } else if (gst_structure_has_name (s, "video/x-h264")) { + SET_META ("H264"); + } else if (gst_structure_has_name (s, "video/x-h265")) { + SET_META ("H265"); + } else if (gst_structure_has_name (s, "video/x-wmv")) { + SET_META ("VC1"); + } else if (gst_structure_has_name (s, "video/x-vp8")) { + SET_META ("VP8"); + } else if (gst_structure_has_name (s, "video/x-vp9")) { + SET_META ("VP9"); + } else if (gst_structure_has_name (s, "video/x-bayer")) { + SET_META ("BAYER"); + } else if (gst_structure_has_name (s, "video/x-sonix")) { + SET_META ("SONIX"); + } else if (gst_structure_has_name (s, "video/x-pwc1")) { + SET_META ("PWC1"); + } else if (gst_structure_has_name (s, "video/x-pwc2")) { + SET_META ("PWC2"); + } else { + /* This code should be kept on sync with the exposed CODEC type of format + * from gstv4l2object.c. This warning will only occure in case we forget + * to also add a format here. */ + gchar *s_str = gst_structure_to_string (s); + g_warning ("Missing fixed name mapping for caps '%s', this is a GStreamer " + "bug, please report at https://bugs.gnome.org", s_str); + g_free (s_str); + } - while (TRUE) { - GstCaps *src_caps, *sink_caps; - gint video_fd; + if (codec_name) { + type_name = g_strdup_printf ("v4l2%sdec", codec_name); + if (g_type_from_name (type_name) != 0) { + g_free (type_name); + type_name = g_strdup_printf ("v4l2%s%sdec", basename, codec_name); + } - g_free (device); - device = g_strdup_printf ("/dev/video%d", ++i); + g_free (codec_name); + } - if (!g_file_test (device, G_FILE_TEST_EXISTS)) - break; + return type_name; +#undef SET_META +} - video_fd = open (device, O_RDWR); - if (video_fd == -1) { - GST_WARNING ("Failed to open %s", device); +void +gst_v4l2_video_dec_register (GstPlugin * plugin, const gchar * basename, + const gchar * device_path, GstCaps * sink_caps, GstCaps * src_caps) +{ + gint i; + + for (i = 0; i < gst_caps_get_size (sink_caps); i++) { + GstV4l2VideoDecCData *cdata; + GstStructure *s; + GTypeQuery type_query; + GTypeInfo type_info = { 0, }; + GType type, subtype; + gchar *type_name; + + s = gst_caps_get_structure (sink_caps, i); + + cdata = g_new0 (GstV4l2VideoDecCData, 1); + cdata->device = g_strdup (device_path); + cdata->sink_caps = gst_caps_new_empty (); + gst_caps_append_structure (cdata->sink_caps, gst_structure_copy (s)); + cdata->src_caps = gst_caps_ref (src_caps); + type_name = gst_v4l2_video_dec_set_metadata (s, cdata, basename); + + /* Skip over if we hit an unmapped type */ + if (!type_name) { + g_free (cdata); continue; } - /* get sink supported format (no MPLANE for codec) */ - sink_caps = gst_v4l2_video_dec_probe_caps (device, video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT, gst_v4l2_object_get_codec_caps ()); - - /* get src supported format */ - src_caps = gst_caps_merge (gst_v4l2_video_dec_probe_caps (device, video_fd, - V4L2_BUF_TYPE_VIDEO_CAPTURE, gst_v4l2_object_get_raw_caps ()), - gst_v4l2_video_dec_probe_caps (device, video_fd, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - gst_v4l2_object_get_raw_caps ())); - - if (!gst_caps_is_empty (sink_caps) && !gst_caps_is_empty (src_caps)) { - GTypeQuery type_query; - GTypeInfo type_info = { 0, }; - GType type, subtype; - gchar *type_name; - Gstv4l2VideoDecQData *qdata; - - type = gst_v4l2_video_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_name = g_strdup_printf ("v4l2video%ddec", i); - subtype = g_type_register_static (type, type_name, &type_info, 0); - - qdata = g_new0 (Gstv4l2VideoDecQData, 1); - qdata->device = g_strdup (device); - qdata->sink_caps = gst_caps_ref (sink_caps); - qdata->src_caps = gst_caps_ref (src_caps); - - g_type_set_qdata (subtype, V4L2_VIDEO_DEC_QUARK, qdata); - - gst_element_register (plugin, type_name, GST_RANK_PRIMARY + 1, subtype); - - g_free (type_name); - } + type = gst_v4l2_video_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_video_dec_subclass_init; + type_info.class_data = cdata; + type_info.instance_init = gst_v4l2_video_dec_subinstance_init; + + subtype = g_type_register_static (type, type_name, &type_info, 0); +#ifdef TIZEN_FEATURE_V4L2VIDEO_ADJ_RANK + if (!gst_element_register (plugin, type_name, GST_RANK_PRIMARY, + subtype)) + GST_WARNING ("Failed to register plugin '%s'", type_name); +#else + if (!gst_element_register (plugin, type_name, GST_RANK_PRIMARY + 1, + subtype)) + GST_WARNING ("Failed to register plugin '%s'", type_name); +#endif - close (video_fd); - gst_caps_unref (src_caps); - gst_caps_unref (sink_caps); + g_free (type_name); } - - g_free (device); - - return TRUE; }