mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-19 08:11:16 +00:00
0e4e351b83
Provides the following elements: qtkitvideosrc: OS X video source relying on the QTKit API. Comes with hard-coded caps as the API does not provide any way of querying for formats supported by the hardware. Hasn't been tested a lot, but seems to work. miovideosrc: OS X video source which uses the undocumented/private CoreMediaIOServices API, which is also the one used by iChat. Present on latest version of Leopard and all versions of Snow Leopard. Has been tested extensively with built-in cameras and TANDBERG's PrecisionHD USB camera. vtenc, vtdec: Generic codec wrappers which make use of the undocumented/ private VideoToolbox API on OS X and iOS. List of codecs are currently hard-coded to H.264 for vtenc, and H.264 + JPEG for vtdec. Can easily be expanded by adding new entries to the lists, but haven't yet had time to do that. Should probably also implement probing as available codecs depend on the OS and its version, and there doesn't seem to be any way to enumerate the available codecs. vth264decbin, vth264encbin: Wrapper bins to make it easier to use vtdec_h264/vtenc_h264 in live scenarios. iphonecamerasrc: iPhone camera source relying on the undocumented/private Celestial API. Tested on iOS 3.1 running on an iPhone 3GS. Stops working after a few minutes, presumably because of a resource leak. Needs some love. Note that the iOS parts haven't yet been ported to iOS 4.x.
773 lines
21 KiB
C
773 lines
21 KiB
C
/*
|
|
* Copyright (C) 2010 Ole André Vadla Ravnås <oleavr@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "iphonecamerasrc.h"
|
|
|
|
#include "coremediabuffer.h"
|
|
|
|
#include <gst/video/video.h>
|
|
|
|
#define DEFAULT_DO_STATS FALSE
|
|
|
|
#define BUFQUEUE_LOCK(instance) GST_OBJECT_LOCK (instance)
|
|
#define BUFQUEUE_UNLOCK(instance) GST_OBJECT_UNLOCK (instance)
|
|
#define BUFQUEUE_WAIT(instance) \
|
|
g_cond_wait (instance->cond, GST_OBJECT_GET_LOCK (instance))
|
|
#define BUFQUEUE_NOTIFY(instance) g_cond_signal (instance->cond)
|
|
|
|
GST_DEBUG_CATEGORY (gst_iphone_camera_src_debug);
|
|
#define GST_CAT_DEFAULT gst_iphone_camera_src_debug
|
|
|
|
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YUY2") ";"
|
|
GST_VIDEO_CAPS_YUV ("I420"))
|
|
);
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_DO_STATS
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
guint index;
|
|
|
|
GstVideoFormat video_format;
|
|
guint32 fourcc;
|
|
gint width;
|
|
gint height;
|
|
gint fps_n;
|
|
gint fps_d;
|
|
} GstIPhoneCameraFormat;
|
|
|
|
static gboolean gst_iphone_camera_src_open_device (GstIPhoneCameraSrc * self);
|
|
static void gst_iphone_camera_src_close_device (GstIPhoneCameraSrc * self);
|
|
static void gst_iphone_camera_src_ensure_device_caps_and_formats
|
|
(GstIPhoneCameraSrc * self);
|
|
static void gst_iphone_camera_src_release_device_caps_and_formats
|
|
(GstIPhoneCameraSrc * self);
|
|
static gboolean gst_iphone_camera_src_select_format (GstIPhoneCameraSrc * self,
|
|
GstIPhoneCameraFormat * format);
|
|
|
|
static gboolean gst_iphone_camera_src_parse_imager_format
|
|
(GstIPhoneCameraSrc * self, guint index, CFDictionaryRef imager_format,
|
|
GstIPhoneCameraFormat * format);
|
|
static FigStatus gst_iphone_camera_src_set_device_property_i32
|
|
(GstIPhoneCameraSrc * self, CFStringRef name, SInt32 value);
|
|
static FigStatus gst_iphone_camera_src_set_device_property_cstr
|
|
(GstIPhoneCameraSrc * self, const gchar * name, const gchar * value);
|
|
|
|
static GstPushSrcClass *parent_class;
|
|
|
|
GST_BOILERPLATE (GstIPhoneCameraSrc, gst_iphone_camera_src, GstPushSrc,
|
|
GST_TYPE_PUSH_SRC);
|
|
|
|
static void
|
|
gst_iphone_camera_src_init (GstIPhoneCameraSrc * self,
|
|
GstIPhoneCameraSrcClass * gclass)
|
|
{
|
|
GstBaseSrc *base_src = GST_BASE_SRC_CAST (self);
|
|
|
|
gst_base_src_set_live (base_src, TRUE);
|
|
gst_base_src_set_format (base_src, GST_FORMAT_TIME);
|
|
|
|
self->cond = g_cond_new ();
|
|
}
|
|
|
|
static void
|
|
gst_iphone_camera_src_dispose (GObject * object)
|
|
{
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gst_iphone_camera_src_finalize (GObject * object)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (object);
|
|
|
|
g_cond_free (self->cond);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gst_iphone_camera_src_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DO_STATS:
|
|
g_value_set_boolean (value, self->do_stats);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_iphone_camera_src_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DO_STATS:
|
|
self->do_stats = g_value_get_boolean (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
gst_iphone_camera_src_change_state (GstElement * element,
|
|
GstStateChange transition)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (element);
|
|
GstStateChangeReturn ret;
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
if (!gst_iphone_camera_src_open_device (self))
|
|
goto open_failed;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
gst_iphone_camera_src_close_device (self);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
|
|
/* ERRORS */
|
|
open_failed:
|
|
{
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
}
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_iphone_camera_src_get_caps (GstBaseSrc * basesrc)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (basesrc);
|
|
GstCaps *result;
|
|
|
|
if (self->device != NULL) {
|
|
gst_iphone_camera_src_ensure_device_caps_and_formats (self);
|
|
|
|
result = gst_caps_ref (self->device_caps);
|
|
} else {
|
|
result = NULL;
|
|
}
|
|
|
|
if (result != NULL) {
|
|
gchar *str;
|
|
|
|
str = gst_caps_to_string (result);
|
|
GST_DEBUG_OBJECT (self, "returning: %s", str);
|
|
g_free (str);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
gst_iphone_camera_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (basesrc);
|
|
GstVideoFormat video_format;
|
|
gint width, height, fps_n, fps_d;
|
|
guint i;
|
|
GstIPhoneCameraFormat *selected_format;
|
|
|
|
if (self->device == NULL)
|
|
goto no_device;
|
|
|
|
if (!gst_video_format_parse_caps (caps, &video_format, &width, &height))
|
|
goto invalid_format;
|
|
if (!gst_video_parse_caps_framerate (caps, &fps_n, &fps_d))
|
|
goto invalid_format;
|
|
|
|
gst_iphone_camera_src_ensure_device_caps_and_formats (self);
|
|
|
|
selected_format = NULL;
|
|
|
|
for (i = 0; i != self->device_formats->len; i++) {
|
|
GstIPhoneCameraFormat *format;
|
|
|
|
format = &g_array_index (self->device_formats, GstIPhoneCameraFormat, i);
|
|
if (format->video_format == video_format &&
|
|
format->width == width && format->height == height &&
|
|
format->fps_n == fps_n && format->fps_d == fps_d) {
|
|
selected_format = format;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (selected_format == NULL)
|
|
goto invalid_format;
|
|
|
|
GST_DEBUG_OBJECT (self, "selecting format %u", selected_format->index);
|
|
|
|
if (!gst_iphone_camera_src_select_format (self, selected_format))
|
|
goto select_failed;
|
|
|
|
gst_iphone_camera_src_release_device_caps_and_formats (self);
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
no_device:
|
|
{
|
|
GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("no device"), (NULL));
|
|
return FALSE;
|
|
}
|
|
invalid_format:
|
|
{
|
|
GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("invalid format"), (NULL));
|
|
return FALSE;
|
|
}
|
|
select_failed:
|
|
{
|
|
GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("failed to select format"),
|
|
(NULL));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_iphone_camera_src_start (GstBaseSrc * basesrc)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (basesrc);
|
|
|
|
self->running = TRUE;
|
|
self->offset = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_iphone_camera_src_stop (GstBaseSrc * basesrc)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_iphone_camera_src_query (GstBaseSrc * basesrc, GstQuery * query)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (basesrc);
|
|
gboolean result = FALSE;
|
|
|
|
switch (GST_QUERY_TYPE (query)) {
|
|
case GST_QUERY_LATENCY:{
|
|
GstClockTime min_latency, max_latency;
|
|
|
|
if (self->device == NULL || !GST_CLOCK_TIME_IS_VALID (self->duration))
|
|
goto beach;
|
|
|
|
min_latency = max_latency = self->duration;
|
|
|
|
GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
|
|
" max %" GST_TIME_FORMAT,
|
|
GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
|
|
|
|
gst_query_set_latency (query, TRUE, min_latency, max_latency);
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
default:
|
|
result = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
|
|
break;
|
|
}
|
|
|
|
beach:
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
gst_iphone_camera_src_unlock (GstBaseSrc * basesrc)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (basesrc);
|
|
|
|
BUFQUEUE_LOCK (self);
|
|
self->running = FALSE;
|
|
BUFQUEUE_UNLOCK (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_iphone_camera_src_unlock_stop (GstBaseSrc * basesrc)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static Boolean
|
|
gst_iphone_camera_src_validate (FigBufferQueueRef queue, FigSampleBuffer * buf,
|
|
void *refCon)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (refCon);
|
|
|
|
BUFQUEUE_LOCK (self);
|
|
self->has_pending = TRUE;
|
|
BUFQUEUE_NOTIFY (self);
|
|
BUFQUEUE_UNLOCK (self);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_iphone_camera_src_create (GstPushSrc * pushsrc, GstBuffer ** buf)
|
|
{
|
|
GstIPhoneCameraSrc *self = GST_IPHONE_CAMERA_SRC_CAST (pushsrc);
|
|
GstCMApi *cm = self->ctx->cm;
|
|
FigSampleBuffer *sbuf = NULL;
|
|
GstClock *clock;
|
|
GstClockTime ts;
|
|
|
|
BUFQUEUE_LOCK (self);
|
|
while (self->running && !self->has_pending)
|
|
BUFQUEUE_WAIT (self);
|
|
sbuf = cm->FigBufferQueueDequeueAndRetain (self->queue);
|
|
self->has_pending = !cm->FigBufferQueueIsEmpty (self->queue);
|
|
BUFQUEUE_UNLOCK (self);
|
|
|
|
if (G_UNLIKELY (!self->running))
|
|
goto shutting_down;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
if ((clock = GST_ELEMENT_CLOCK (self)) != NULL) {
|
|
ts = gst_clock_get_time (clock);
|
|
|
|
if (ts > GST_ELEMENT (self)->base_time)
|
|
ts -= GST_ELEMENT (self)->base_time;
|
|
else
|
|
ts = 0;
|
|
|
|
if (ts > self->duration)
|
|
ts -= self->duration;
|
|
else
|
|
ts = 0;
|
|
} else {
|
|
ts = GST_CLOCK_TIME_NONE;
|
|
}
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
*buf = gst_core_media_buffer_new (self->ctx, sbuf);
|
|
GST_BUFFER_OFFSET (*buf) = self->offset;
|
|
GST_BUFFER_OFFSET_END (*buf) = self->offset + 1;
|
|
GST_BUFFER_TIMESTAMP (*buf) = ts;
|
|
GST_BUFFER_DURATION (*buf) = self->duration;
|
|
|
|
if (self->offset == 0) {
|
|
GST_BUFFER_FLAG_SET (*buf, GST_BUFFER_FLAG_DISCONT);
|
|
}
|
|
self->offset++;
|
|
|
|
cm->FigSampleBufferRelease (sbuf);
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
/* ERRORS */
|
|
shutting_down:
|
|
{
|
|
cm->FigSampleBufferRelease (sbuf);
|
|
return GST_FLOW_WRONG_STATE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_iphone_camera_src_open_device (GstIPhoneCameraSrc * self)
|
|
{
|
|
GstCoreMediaCtx *ctx = NULL;
|
|
GError *error = NULL;
|
|
GstCMApi *cm = NULL;
|
|
GstMTApi *mt = NULL;
|
|
GstCelApi *cel = NULL;
|
|
FigStatus status;
|
|
FigCaptureDeviceRef device = NULL;
|
|
FigBaseObjectRef device_base;
|
|
FigBaseVTable *device_vt;
|
|
FigCaptureStreamRef stream = NULL;
|
|
FigBaseObjectRef stream_base;
|
|
FigBaseVTable *stream_vt;
|
|
FigCaptureStreamIface *stream_iface;
|
|
FigBufferQueueRef queue = NULL;
|
|
|
|
ctx = gst_core_media_ctx_new (GST_API_CORE_VIDEO | GST_API_CORE_MEDIA
|
|
| GST_API_MEDIA_TOOLBOX | GST_API_CELESTIAL, &error);
|
|
if (error != NULL)
|
|
goto api_error;
|
|
cm = ctx->cm;
|
|
mt = ctx->mt;
|
|
cel = ctx->cel;
|
|
|
|
status = cel->FigCreateCaptureDevicesAndStreamsForPreset (NULL,
|
|
*(cel->kFigRecorderCapturePreset_VideoRecording), NULL,
|
|
&device, &stream, NULL, NULL);
|
|
if (status == kFigResourceBusy)
|
|
goto device_busy;
|
|
else if (status != kFigSuccess)
|
|
goto unexpected_error;
|
|
|
|
device_base = mt->FigCaptureDeviceGetFigBaseObject (device);
|
|
device_vt = cm->FigBaseObjectGetVTable (device_base);
|
|
|
|
stream_base = mt->FigCaptureStreamGetFigBaseObject (stream);
|
|
stream_vt = cm->FigBaseObjectGetVTable (stream_base);
|
|
stream_iface = stream_vt->derived;
|
|
|
|
status = stream_vt->base->CopyProperty (stream_base,
|
|
*(mt->kFigCaptureStreamProperty_BufferQueue), NULL, &queue);
|
|
if (status != kFigSuccess)
|
|
goto unexpected_error;
|
|
|
|
self->has_pending = FALSE;
|
|
|
|
cm->FigBufferQueueSetValidationCallback (queue,
|
|
gst_iphone_camera_src_validate, self);
|
|
|
|
self->ctx = ctx;
|
|
|
|
self->device = device;
|
|
self->device_iface_base = device_vt->base;
|
|
self->stream = stream;
|
|
self->stream_iface_base = stream_vt->base;
|
|
self->stream_iface = stream_iface;
|
|
self->queue = queue;
|
|
|
|
self->duration = GST_CLOCK_TIME_NONE;
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
api_error:
|
|
{
|
|
GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("API error"),
|
|
("%s", error->message));
|
|
g_clear_error (&error);
|
|
goto any_error;
|
|
}
|
|
device_busy:
|
|
{
|
|
GST_ELEMENT_ERROR (self, RESOURCE, BUSY,
|
|
("device is already in use"), (NULL));
|
|
goto any_error;
|
|
}
|
|
unexpected_error:
|
|
{
|
|
GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
|
|
("unexpected error while opening device (%d)", status), (NULL));
|
|
goto any_error;
|
|
}
|
|
any_error:
|
|
{
|
|
if (stream != NULL)
|
|
CFRelease (stream);
|
|
if (device != NULL)
|
|
CFRelease (device);
|
|
|
|
if (ctx != NULL) {
|
|
cm->FigBufferQueueRelease (queue);
|
|
g_object_unref (ctx);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_iphone_camera_src_close_device (GstIPhoneCameraSrc * self)
|
|
{
|
|
gst_iphone_camera_src_release_device_caps_and_formats (self);
|
|
|
|
self->stream_iface->Stop (self->stream);
|
|
self->stream_iface = NULL;
|
|
self->stream_iface_base->Finalize (self->stream);
|
|
self->stream_iface_base = NULL;
|
|
CFRelease (self->stream);
|
|
self->stream = NULL;
|
|
|
|
self->device_iface_base->Finalize (self->device);
|
|
self->device_iface_base = NULL;
|
|
CFRelease (self->device);
|
|
self->device = NULL;
|
|
|
|
self->ctx->cm->FigBufferQueueRelease (self->queue);
|
|
self->queue = NULL;
|
|
|
|
g_object_unref (self->ctx);
|
|
self->ctx = NULL;
|
|
}
|
|
|
|
static void
|
|
gst_iphone_camera_src_ensure_device_caps_and_formats (GstIPhoneCameraSrc * self)
|
|
{
|
|
FigStatus status;
|
|
CFArrayRef iformats = NULL;
|
|
CFIndex format_count, i;
|
|
|
|
if (self->device_caps != NULL)
|
|
goto already_probed;
|
|
|
|
self->device_caps = gst_caps_new_empty ();
|
|
self->device_formats =
|
|
g_array_new (FALSE, FALSE, sizeof (GstIPhoneCameraFormat));
|
|
|
|
status = self->device_iface_base->CopyProperty (self->device,
|
|
*(self->ctx->mt->kFigCaptureDeviceProperty_ImagerSupportedFormatsArray),
|
|
NULL, (CFTypeRef *) & iformats);
|
|
if (status != kFigSuccess)
|
|
goto beach;
|
|
|
|
format_count = CFArrayGetCount (iformats);
|
|
GST_DEBUG_OBJECT (self, "device supports %d formats", (gint) format_count);
|
|
|
|
for (i = 0; i != format_count; i++) {
|
|
CFDictionaryRef iformat;
|
|
GstIPhoneCameraFormat format;
|
|
|
|
iformat = CFArrayGetValueAtIndex (iformats, i);
|
|
|
|
if (gst_iphone_camera_src_parse_imager_format (self, i, iformat, &format)) {
|
|
gst_caps_append_structure (self->device_caps,
|
|
gst_structure_new ("video/x-raw-yuv",
|
|
"format", GST_TYPE_FOURCC, format.fourcc,
|
|
"width", G_TYPE_INT, format.width,
|
|
"height", G_TYPE_INT, format.height,
|
|
"framerate", GST_TYPE_FRACTION, format.fps_n, format.fps_d,
|
|
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL));
|
|
g_array_append_val (self->device_formats, format);
|
|
} else {
|
|
GST_WARNING_OBJECT (self, "ignoring unknown format #%d", (gint) i);
|
|
}
|
|
}
|
|
|
|
CFRelease (iformats);
|
|
|
|
already_probed:
|
|
beach:
|
|
return;
|
|
}
|
|
|
|
static void
|
|
gst_iphone_camera_src_release_device_caps_and_formats (GstIPhoneCameraSrc *
|
|
self)
|
|
{
|
|
if (self->device_caps != NULL) {
|
|
gst_caps_unref (self->device_caps);
|
|
self->device_caps = NULL;
|
|
}
|
|
|
|
if (self->device_formats != NULL) {
|
|
g_array_free (self->device_formats, TRUE);
|
|
self->device_formats = NULL;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_iphone_camera_src_select_format (GstIPhoneCameraSrc * self,
|
|
GstIPhoneCameraFormat * format)
|
|
{
|
|
gboolean result = FALSE;
|
|
GstMTApi *mt = self->ctx->mt;
|
|
FigStatus status;
|
|
SInt32 framerate;
|
|
|
|
status = gst_iphone_camera_src_set_device_property_i32 (self,
|
|
*(mt->kFigCaptureDeviceProperty_ImagerFormatDescription), format->index);
|
|
if (status != kFigSuccess)
|
|
goto beach;
|
|
|
|
framerate = format->fps_n / format->fps_d;
|
|
|
|
status = gst_iphone_camera_src_set_device_property_i32 (self,
|
|
*(mt->kFigCaptureDeviceProperty_ImagerFrameRate), framerate);
|
|
if (status != kFigSuccess)
|
|
goto beach;
|
|
|
|
status = gst_iphone_camera_src_set_device_property_i32 (self,
|
|
*(mt->kFigCaptureDeviceProperty_ImagerMinimumFrameRate), framerate);
|
|
if (status != kFigSuccess)
|
|
goto beach;
|
|
|
|
status = gst_iphone_camera_src_set_device_property_cstr (self,
|
|
"ColorRange", "ColorRangeSDVideo");
|
|
if (status != kFigSuccess)
|
|
goto beach;
|
|
|
|
status = self->stream_iface->Start (self->stream);
|
|
if (status != kFigSuccess)
|
|
goto beach;
|
|
|
|
GST_DEBUG_OBJECT (self, "configured format %d (%d x %d @ %d Hz)",
|
|
format->index, format->width, format->height, (gint) framerate);
|
|
|
|
self->duration =
|
|
gst_util_uint64_scale (GST_SECOND, format->fps_d, format->fps_n);
|
|
|
|
result = TRUE;
|
|
|
|
beach:
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
gst_iphone_camera_src_parse_imager_format (GstIPhoneCameraSrc * self,
|
|
guint index, CFDictionaryRef imager_format, GstIPhoneCameraFormat * format)
|
|
{
|
|
GstCMApi *cm = self->ctx->cm;
|
|
GstMTApi *mt = self->ctx->mt;
|
|
const FigFormatDescription *desc;
|
|
FigVideoDimensions dim;
|
|
UInt32 subtype;
|
|
CFNumberRef framerate_value;
|
|
SInt32 fps_n;
|
|
|
|
format->index = index;
|
|
|
|
desc = CFDictionaryGetValue (imager_format,
|
|
*(mt->kFigImagerSupportedFormat_FormatDescription));
|
|
|
|
dim = cm->FigVideoFormatDescriptionGetDimensions (desc);
|
|
format->width = dim.width;
|
|
format->height = dim.height;
|
|
|
|
subtype = cm->FigFormatDescriptionGetMediaSubType (desc);
|
|
|
|
switch (subtype) {
|
|
case kComponentVideoUnsigned:
|
|
format->video_format = GST_VIDEO_FORMAT_YUY2;
|
|
format->fourcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
|
|
break;
|
|
case kYUV420vCodecType:
|
|
format->video_format = GST_VIDEO_FORMAT_I420;
|
|
format->fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0');
|
|
break;
|
|
default:
|
|
goto unsupported_format;
|
|
}
|
|
|
|
framerate_value = CFDictionaryGetValue (imager_format,
|
|
*(mt->kFigImagerSupportedFormat_MaxFrameRate));
|
|
CFNumberGetValue (framerate_value, kCFNumberSInt32Type, &fps_n);
|
|
format->fps_n = fps_n;
|
|
format->fps_d = 1;
|
|
|
|
return TRUE;
|
|
|
|
unsupported_format:
|
|
return FALSE;
|
|
}
|
|
|
|
static FigStatus
|
|
gst_iphone_camera_src_set_device_property_i32 (GstIPhoneCameraSrc * self,
|
|
CFStringRef name, SInt32 value)
|
|
{
|
|
FigStatus status;
|
|
CFNumberRef number;
|
|
|
|
number = CFNumberCreate (NULL, kCFNumberSInt32Type, &value);
|
|
status = self->device_iface_base->SetProperty (self->device, name, number);
|
|
CFRelease (number);
|
|
|
|
return status;
|
|
}
|
|
|
|
static FigStatus
|
|
gst_iphone_camera_src_set_device_property_cstr (GstIPhoneCameraSrc * self,
|
|
const gchar * name, const gchar * value)
|
|
{
|
|
FigStatus status;
|
|
CFStringRef name_str, value_str;
|
|
|
|
name_str = CFStringCreateWithCStringNoCopy (NULL, name,
|
|
kCFStringEncodingUTF8, kCFAllocatorNull);
|
|
value_str = CFStringCreateWithCStringNoCopy (NULL, value,
|
|
kCFStringEncodingUTF8, kCFAllocatorNull);
|
|
status = self->device_iface_base->SetProperty (self->device,
|
|
name_str, value_str);
|
|
CFRelease (value_str);
|
|
CFRelease (name_str);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
gst_iphone_camera_src_base_init (gpointer gclass)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
|
|
|
|
gst_element_class_set_details_simple (element_class,
|
|
"iPhone camera source",
|
|
"Source/Video",
|
|
"Stream data from iPhone camera sensor",
|
|
"Ole André Vadla Ravnås <oleavr@gmail.com>");
|
|
|
|
gst_element_class_add_pad_template (element_class,
|
|
gst_static_pad_template_get (&src_template));
|
|
}
|
|
|
|
static void
|
|
gst_iphone_camera_src_class_init (GstIPhoneCameraSrcClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
|
GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
|
|
GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
|
|
|
|
gobject_class->dispose = gst_iphone_camera_src_dispose;
|
|
gobject_class->finalize = gst_iphone_camera_src_finalize;
|
|
gobject_class->get_property = gst_iphone_camera_src_get_property;
|
|
gobject_class->set_property = gst_iphone_camera_src_set_property;
|
|
|
|
gstelement_class->change_state = gst_iphone_camera_src_change_state;
|
|
|
|
gstbasesrc_class->get_caps = gst_iphone_camera_src_get_caps;
|
|
gstbasesrc_class->set_caps = gst_iphone_camera_src_set_caps;
|
|
gstbasesrc_class->start = gst_iphone_camera_src_start;
|
|
gstbasesrc_class->stop = gst_iphone_camera_src_stop;
|
|
gstbasesrc_class->query = gst_iphone_camera_src_query;
|
|
gstbasesrc_class->unlock = gst_iphone_camera_src_unlock;
|
|
gstbasesrc_class->unlock_stop = gst_iphone_camera_src_unlock_stop;
|
|
|
|
gstpushsrc_class->create = gst_iphone_camera_src_create;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_DO_STATS,
|
|
g_param_spec_boolean ("do-stats", "Enable statistics",
|
|
"Enable logging of statistics", DEFAULT_DO_STATS,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_iphone_camera_src_debug, "iphonecamerasrc",
|
|
0, "iPhone video source");
|
|
}
|