diff --git a/configure.ac b/configure.ac index eade47da4a..a525fe0c3e 100644 --- a/configure.ac +++ b/configure.ac @@ -534,6 +534,22 @@ case "$host" in ;; esac +dnl *** OS/X AVCVideoServices *** +translit(dnm, m, l) AM_CONDITIONAL(USE_AVC, true) +HAVE_AVC="no" +AG_GST_CHECK_FEATURE(AVC, [AVC Video Services], avcsrc, [ + AC_CHECK_TYPE([AVCDevice], HAVE_AVC="yes", HAVE_AVC="no", + [#include ]) +]) +dnl in case header AVCVideoServices/AVCVideoServices.h is found on other platforms +case "$host" in + *-*darwin*) + ;; + *) + HAVE_AVC="no" + ;; +esac + dnl check for QuickTime translit(dnm, m, l) AM_CONDITIONAL(USE_QUICKTIME, true) AG_GST_CHECK_FEATURE(QUICKTIME, [QuickTime wrapper], qtwrapper, [ @@ -1836,6 +1852,7 @@ sys/dshowdecwrapper/Makefile sys/acmenc/Makefile sys/acmmp3dec/Makefile sys/applemedia/Makefile +sys/avc/Makefile sys/decklink/Makefile sys/directdraw/Makefile sys/directsound/Makefile diff --git a/sys/Makefile.am b/sys/Makefile.am index 8c161d0fe1..7fa59ae0cc 100644 --- a/sys/Makefile.am +++ b/sys/Makefile.am @@ -101,9 +101,15 @@ else SHM_DIR= endif -SUBDIRS = $(ACM_DIR) $(APPLE_MEDIA_DIR) $(DECKLINK_DIR) $(DIRECTDRAW_DIR) $(DIRECTSOUND_DIR) $(DVB_DIR) $(FBDEV_DIR) $(LINSYS_DIR) $(OSX_VIDEO_DIR) $(QT_DIR) $(SHM_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) +if USE_AVC +AVC_DIR=avc +else +AVC_DIR= +endif -DIST_SUBDIRS = acmenc acmmp3dec applemedia decklink directdraw directsound dvb linsys fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \ +SUBDIRS = $(ACM_DIR) $(APPLE_MEDIA_DIR) $(AVC_DIR) $(DECKLINK_DIR) $(DIRECTDRAW_DIR) $(DIRECTSOUND_DIR) $(DVB_DIR) $(FBDEV_DIR) $(LINSYS_DIR) $(OSX_VIDEO_DIR) $(QT_DIR) $(SHM_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) + +DIST_SUBDIRS = acmenc acmmp3dec applemedia avc decklink directdraw directsound dvb linsys fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \ osxvideo qtwrapper shm vcd vdpau wasapi wininet winks winscreencap include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/sys/avc/Makefile.am b/sys/avc/Makefile.am new file mode 100644 index 0000000000..050e3389be --- /dev/null +++ b/sys/avc/Makefile.am @@ -0,0 +1,24 @@ + +plugin_LTLIBRARIES = libgstavc.la + +libgstavc_la_SOURCES = gstavcplugin.cpp gstavcsrc.cpp +libgstavc_la_CPPFLAGS = \ + $(GST_PLUGINS_BAD_CXXFLAGS) \ + $(GST_PLUGINS_BASE_CXXFLAGS) \ + $(GST_CXXFLAGS) \ + -Wno-deprecated-declarations +libgstavc_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR) \ + -lgstinterfaces-$(GST_MAJORMINOR) \ + $(GST_BASE_LIBS) \ + $(GST_LIBS) + +libgstavc_la_LIBTOOLFLAGS = --tag=disable-static + +libgstavc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) \ + -Wl,-framework -Wl,AVCVideoServices \ + -Wl,-framework -Wl,Cocoa \ + -Wl,-framework -Wl,QuickTime + +noinst_HEADERS = gstavcsrc.h + diff --git a/sys/avc/gstavcplugin.cpp b/sys/avc/gstavcplugin.cpp new file mode 100644 index 0000000000..0763210809 --- /dev/null +++ b/sys/avc/gstavcplugin.cpp @@ -0,0 +1,41 @@ +/* GStreamer + * Copyright (C) 2011 David Schleef + * + * 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 Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstavcsrc.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + + gst_element_register (plugin, "avcsrc", GST_RANK_NONE, + gst_avc_src_get_type ()); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "avcsrc", + "AVC Video Services plugin", + plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/sys/avc/gstavcsrc.cpp b/sys/avc/gstavcsrc.cpp new file mode 100644 index 0000000000..8fb1b0345b --- /dev/null +++ b/sys/avc/gstavcsrc.cpp @@ -0,0 +1,408 @@ +/* GStreamer + * Copyright (C) 2011 David Schleef + * + * 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 Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ +/** + * SECTION:element-gstavcsrc + * + * The avcsrc element captures video from an OS/X AVC Video Services + * devices, typically a FireWire camera. + * + * + * Example launch line + * |[ + * gst-launch -v avcsrc ! decodebin ! osxvideosink + * ]| + * + * This pipeline captures from an AVC source, decodes the stream (either + * DV or HDV), and displays the video. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +//#define ENABLE +#ifdef ENABLE +#include +using namespace AVS; +#endif + +#include +#include +#include "gstavcsrc.h" + +GST_DEBUG_CATEGORY_STATIC (gst_avc_src_debug_category); +#define GST_CAT_DEFAULT gst_avc_src_debug_category + +/* prototypes */ + + +static void gst_avc_src_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec); +static void gst_avc_src_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec); +static void gst_avc_src_dispose (GObject * object); +static void gst_avc_src_finalize (GObject * object); + +static GstCaps *gst_avc_src_get_caps (GstBaseSrc * src); +static gboolean gst_avc_src_start (GstBaseSrc * src); +static gboolean gst_avc_src_stop (GstBaseSrc * src); +static gboolean gst_avc_src_is_seekable (GstBaseSrc * src); +static gboolean gst_avc_src_unlock (GstBaseSrc * src); +static gboolean gst_avc_src_event (GstBaseSrc * src, GstEvent * event); +static GstFlowReturn +gst_avc_src_create (GstBaseSrc * src, guint64 offset, guint size, + GstBuffer ** buf); +static gboolean gst_avc_src_query (GstBaseSrc * src, GstQuery * query); +static gboolean gst_avc_src_unlock_stop (GstBaseSrc * src); + +enum +{ + PROP_0 +}; + +/* pad templates */ + +static GstStaticPadTemplate gst_avc_src_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("video/dv,systemstream=true;video/mpegts,systemstream=true,packetsize=188") + ); + + +/* class initialization */ + +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_avc_src_debug_category, "avcsrc", 0, \ + "debug category for avcsrc element"); + +GST_BOILERPLATE_FULL (GstAVCSrc, gst_avc_src, GstBaseSrc, + GST_TYPE_BASE_SRC, DEBUG_INIT); + +static void +gst_avc_src_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_avc_src_src_template)); + + gst_element_class_set_details_simple (element_class, + "AVC Video Services Source", "Video/Source", + "Captures DV or HDV video from Firewire port", + "David Schleef "); +} + +static void +gst_avc_src_class_init (GstAVCSrcClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseSrcClass *base_src_class = GST_BASE_SRC_CLASS (klass); + + gobject_class->set_property = gst_avc_src_set_property; + gobject_class->get_property = gst_avc_src_get_property; + gobject_class->dispose = gst_avc_src_dispose; + gobject_class->finalize = gst_avc_src_finalize; + base_src_class->get_caps = GST_DEBUG_FUNCPTR (gst_avc_src_get_caps); + base_src_class->start = GST_DEBUG_FUNCPTR (gst_avc_src_start); + base_src_class->stop = GST_DEBUG_FUNCPTR (gst_avc_src_stop); + base_src_class->is_seekable = GST_DEBUG_FUNCPTR (gst_avc_src_is_seekable); + base_src_class->unlock = GST_DEBUG_FUNCPTR (gst_avc_src_unlock); + base_src_class->event = GST_DEBUG_FUNCPTR (gst_avc_src_event); + base_src_class->create = GST_DEBUG_FUNCPTR (gst_avc_src_create); + if (0) + base_src_class->query = GST_DEBUG_FUNCPTR (gst_avc_src_query); + if (0) + base_src_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_avc_src_unlock_stop); + +} + +static void +gst_avc_src_init (GstAVCSrc * avcsrc, GstAVCSrcClass * avcsrc_class) +{ + + avcsrc->srcpad = gst_pad_new_from_static_template (&gst_avc_src_src_template, + "src"); + + avcsrc->queue = gst_atomic_queue_new (16); + avcsrc->cond = g_cond_new (); + avcsrc->queue_lock = g_mutex_new (); +} + +void +gst_avc_src_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + /* GstAVCSrc *avcsrc = GST_AVC_SRC (object); */ + + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +void +gst_avc_src_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + /* GstAVCSrc *avcsrc = GST_AVC_SRC (object); */ + + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +void +gst_avc_src_dispose (GObject * object) +{ + /* GstAVCSrc *avcsrc = GST_AVC_SRC (object); */ + + /* clean up as possible. may be called multiple times */ + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +void +gst_avc_src_finalize (GObject * object) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (object); + + /* clean up object here */ + gst_atomic_queue_unref (avcsrc->queue); + g_cond_free (avcsrc->cond); + g_mutex_free (avcsrc->queue_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static GstCaps * +gst_avc_src_get_caps (GstBaseSrc * src) +{ + /* GstAVCSrc *avcsrc = GST_AVC_SRC (src); */ + + return gst_caps_from_string ("video/mpegts,systemstream=true,packetsize=188"); +} + +#define kNumCyclesInMPEGReceiverSegment 20 +#define kNumSegmentsInMPEGReceiverProgram 100 + +#ifdef ENABLE +void +MPEGReceiverMessageReceivedProc (UInt32 msg, UInt32 param1, UInt32 param2, + void *pRefCon) +{ + +} + +IOReturn +MyStructuredDataPushProc (UInt32 CycleDataCount, + MPEGReceiveCycleData * pCycleData, void *pRefCon) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (pRefCon); + + if (avcsrc) { + for (int cycle = 0; cycle < CycleDataCount; cycle++) { + for (int sourcePacket = 0; sourcePacket < pCycleData[cycle].tsPacketCount; + sourcePacket++) { + GstBuffer *buffer; + + buffer = gst_buffer_new_and_alloc (kMPEG2TSPacketSize); + memcpy (GST_BUFFER_DATA (buffer), + pCycleData[cycle].pBuf[sourcePacket], kMPEG2TSPacketSize); + + gst_atomic_queue_push (avcsrc->queue, buffer); + } + } + + g_mutex_lock (avcsrc->queue_lock); + g_cond_signal (avcsrc->cond); + g_mutex_unlock (avcsrc->queue_lock); + } + + return 0; +} +#endif + +static gboolean +gst_avc_src_start (GstBaseSrc * src) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (src); +#ifdef ENABLE + AVCDeviceController *pAVCDeviceController = nil; + AVCDevice *pAVCDevice; + AVCDeviceStream *pAVCDeviceStream; + int deviceIndex = 0; +#endif + + GST_DEBUG_OBJECT (avcsrc, "start"); + + avcsrc->unlock = FALSE; + +#ifdef ENABLE + // Create a AVCDeviceController + CreateAVCDeviceController (&pAVCDeviceController); + if (!pAVCDeviceController) { + // TODO: This should never happen (unless we've run out of memory), but we should handle it cleanly anyway + GST_ERROR ("Failed to create AVC device controller."); + return FALSE; + } + + GST_INFO ("Created AVC device controller."); + + if (deviceIndex >= CFArrayGetCount (pAVCDeviceController->avcDeviceArray)) { + GST_ERROR ("Failed to find AVC device %d", deviceIndex); + return FALSE; + } + + pAVCDevice = (AVCDevice *) + CFArrayGetValueAtIndex (pAVCDeviceController->avcDeviceArray, + deviceIndex); + + if (!pAVCDevice) { + GST_ERROR ("Failed to find AVC device %d", deviceIndex); + return FALSE; + } + + GST_INFO ("Found device with GUID 0x%016llX\n", pAVCDevice->guid); + + pAVCDevice->openDevice (nil, nil); + + pAVCDeviceStream = pAVCDevice->CreateMPEGReceiverForDevicePlug (0, nil, // We'll install the structured callback later (MyStructuredDataPushProc), + nil, + MPEGReceiverMessageReceivedProc, + nil, + nil, kNumCyclesInMPEGReceiverSegment, kNumSegmentsInMPEGReceiverProgram); + + pAVCDeviceStream->pMPEGReceiver->registerStructuredDataPushCallback + (MyStructuredDataPushProc, + kNumCyclesInMPEGReceiverSegment, (void *) avcsrc); + + pAVCDevice->StartAVCDeviceStream (pAVCDeviceStream); +#endif + + return TRUE; +} + +static gboolean +gst_avc_src_stop (GstBaseSrc * src) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (src); + GstBuffer *buffer; + + GST_DEBUG_OBJECT (avcsrc, "stop"); + + /* FIXME do whatever is needed to stop capture */ + + while ((buffer = GST_BUFFER (gst_atomic_queue_pop (avcsrc->queue))) != NULL) { + gst_buffer_unref (buffer); + } + + return TRUE; +} + +static gboolean +gst_avc_src_is_seekable (GstBaseSrc * src) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (src); + + GST_DEBUG_OBJECT (avcsrc, "is_seekable"); + + return FALSE; +} + +static gboolean +gst_avc_src_unlock (GstBaseSrc * src) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (src); + + GST_DEBUG_OBJECT (avcsrc, "unlock"); + + g_mutex_lock (avcsrc->queue_lock); + avcsrc->unlock = TRUE; + g_cond_signal (avcsrc->cond); + g_mutex_unlock (avcsrc->queue_lock); + + return TRUE; +} + +static gboolean +gst_avc_src_event (GstBaseSrc * src, GstEvent * event) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (src); + + GST_DEBUG_OBJECT (avcsrc, "event"); + + return TRUE; +} + +static GstFlowReturn +gst_avc_src_create (GstBaseSrc * src, guint64 offset, guint size, + GstBuffer ** buf) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (src); + GstBuffer *buffer; + + GST_DEBUG_OBJECT (avcsrc, "create"); + + g_mutex_lock (avcsrc->queue_lock); + buffer = GST_BUFFER (gst_atomic_queue_pop (avcsrc->queue)); + while (buffer == NULL && !avcsrc->unlock) { + g_cond_wait (avcsrc->cond, avcsrc->queue_lock); + buffer = GST_BUFFER (gst_atomic_queue_pop (avcsrc->queue)); + } + g_mutex_unlock (avcsrc->queue_lock); + + if (avcsrc->unlock) { + if (buffer) + gst_buffer_unref (buffer); + return GST_FLOW_WRONG_STATE; + } + + gst_buffer_set_caps (buffer, GST_PAD_CAPS (avcsrc->srcpad)); + + *buf = buffer; + + return GST_FLOW_OK; +} + +static gboolean +gst_avc_src_query (GstBaseSrc * src, GstQuery * query) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (src); + + GST_DEBUG_OBJECT (avcsrc, "query"); + + return TRUE; +} + +static gboolean +gst_avc_src_unlock_stop (GstBaseSrc * src) +{ + GstAVCSrc *avcsrc = GST_AVC_SRC (src); + + GST_DEBUG_OBJECT (avcsrc, "stop"); + + return TRUE; +} diff --git a/sys/avc/gstavcsrc.h b/sys/avc/gstavcsrc.h new file mode 100644 index 0000000000..bcf14f4c65 --- /dev/null +++ b/sys/avc/gstavcsrc.h @@ -0,0 +1,57 @@ +/* GStreamer + * Copyright (C) 2011 FIXME + * + * 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. + */ + +#ifndef _GST_AVC_SRC_H_ +#define _GST_AVC_SRC_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_AVC_SRC (gst_avc_src_get_type()) +#define GST_AVC_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVC_SRC,GstAVCSrc)) +#define GST_AVC_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVC_SRC,GstAVCSrcClass)) +#define GST_IS_AVC_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVC_SRC)) +#define GST_IS_AVC_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVC_SRC)) + +typedef struct _GstAVCSrc GstAVCSrc; +typedef struct _GstAVCSrcClass GstAVCSrcClass; + +struct _GstAVCSrc +{ + GstBaseSrc base_avcsrc; + + GstPad *srcpad; + + GstAtomicQueue *queue; + GCond *cond; + GMutex *queue_lock; + gboolean unlock; +}; + +struct _GstAVCSrcClass +{ + GstBaseSrcClass base_avcsrc_class; +}; + +GType gst_avc_src_get_type (void); + +G_END_DECLS + +#endif