mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-07 07:58:51 +00:00
sys/osxaudio/: Rewrite osxaudio to work more flexibly and more reliably, using a different abstraction layer of corea...
Original commit message from CVS: Patch by: Justin Karnegas <justin@affinix.com> and Michael Smith <msmith@songbirdnest.com> * sys/osxaudio/gstosxaudio.c: * sys/osxaudio/gstosxaudioelement.c: * sys/osxaudio/gstosxaudioelement.h: * sys/osxaudio/gstosxaudiosink.c: * sys/osxaudio/gstosxaudiosink.h: * sys/osxaudio/gstosxaudiosrc.c: * sys/osxaudio/gstosxaudiosrc.h: * sys/osxaudio/gstosxringbuffer.c: * sys/osxaudio/gstosxringbuffer.h: Rewrite osxaudio to work more flexibly and more reliably, using a different abstraction layer of coreaudio that is the recommended way of doing low-level audio I/O on OSX. Fixes byg #564948.
This commit is contained in:
parent
996fb72681
commit
17bb67f873
10 changed files with 741 additions and 451 deletions
18
ChangeLog
18
ChangeLog
|
@ -1,3 +1,21 @@
|
||||||
|
2009-01-02 Michael Smith <msmith@songbirdnest.com>
|
||||||
|
|
||||||
|
Patch by: Justin Karnegas <justin@affinix.com> and
|
||||||
|
Michael Smith <msmith@songbirdnest.com>
|
||||||
|
* sys/osxaudio/gstosxaudio.c:
|
||||||
|
* sys/osxaudio/gstosxaudioelement.c:
|
||||||
|
* sys/osxaudio/gstosxaudioelement.h:
|
||||||
|
* sys/osxaudio/gstosxaudiosink.c:
|
||||||
|
* sys/osxaudio/gstosxaudiosink.h:
|
||||||
|
* sys/osxaudio/gstosxaudiosrc.c:
|
||||||
|
* sys/osxaudio/gstosxaudiosrc.h:
|
||||||
|
* sys/osxaudio/gstosxringbuffer.c:
|
||||||
|
* sys/osxaudio/gstosxringbuffer.h:
|
||||||
|
Rewrite osxaudio to work more flexibly and more reliably, using a
|
||||||
|
different abstraction layer of coreaudio that is the recommended way of
|
||||||
|
doing low-level audio I/O on OSX.
|
||||||
|
Fixes byg #564948.
|
||||||
|
|
||||||
2009-01-02 Wim Taymans <wim.taymans@collabora.co.uk>
|
2009-01-02 Wim Taymans <wim.taymans@collabora.co.uk>
|
||||||
|
|
||||||
* tests/examples/rtp/server-decodebin-H263p-AMR.sh:
|
* tests/examples/rtp/server-decodebin-H263p-AMR.sh:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/* GStreamer
|
/*
|
||||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
* GStreamer
|
||||||
|
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
* Copyright (C) 2007,2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
* Copyright (C) 2007,2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
|
@ -16,10 +17,10 @@
|
||||||
* License along with this library; if not, write to the
|
* License along with this library; if not, write to the
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*
|
*
|
||||||
* The development of this code was made possible due to the involvement of
|
* The development of this code was made possible due to the involvement of
|
||||||
* Pioneers of the Inevitable, the creators of the Songbird Music player
|
* Pioneers of the Inevitable, the creators of the Songbird Music player
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,12 +43,10 @@
|
||||||
* Last reviewed on 2006-03-01 (0.10.4)
|
* Last reviewed on 2006-03-01 (0.10.4)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include "gstosxaudioelement.h"
|
#include "gstosxaudioelement.h"
|
||||||
#include "gstosxaudiosink.h"
|
#include "gstosxaudiosink.h"
|
||||||
#include "gstosxaudiosrc.h"
|
#include "gstosxaudiosrc.h"
|
||||||
|
@ -55,7 +54,6 @@
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_init (GstPlugin * plugin)
|
plugin_init (GstPlugin * plugin)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!gst_element_register (plugin, "osxaudiosink", GST_RANK_PRIMARY,
|
if (!gst_element_register (plugin, "osxaudiosink", GST_RANK_PRIMARY,
|
||||||
GST_TYPE_OSX_AUDIO_SINK)) {
|
GST_TYPE_OSX_AUDIO_SINK)) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
* Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
||||||
* Copyright 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
* Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* to deal in the Software without restriction, including without limitation
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
@ -40,9 +40,10 @@
|
||||||
* License along with this library; if not, write to the
|
* License along with this library; if not, write to the
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*
|
*
|
||||||
* The development of this code was made possible due to the involvement of Pioneers of the * Inevitable, the creators of the Songbird Music player
|
* The development of this code was made possible due to the involvement of
|
||||||
*
|
* Pioneers of the Inevitable, the creators of the Songbird Music player
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
@ -67,12 +68,11 @@ gst_osx_audio_element_get_type ()
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
gst_osxaudioelement_type = g_type_register_static (G_TYPE_INTERFACE,
|
gst_osxaudioelement_type = g_type_register_static (G_TYPE_INTERFACE,
|
||||||
"GstOsxAudioElement", &gst_osxaudioelement_info, 0);
|
"GstOsxAudioElement", &gst_osxaudioelement_info, 0);
|
||||||
/*g_type_interface_add_prerequisite (gst_osxaudioelement_type,
|
|
||||||
GST_TYPE_IMPLEMENTS_INTERFACE); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return gst_osxaudioelement_type;
|
return gst_osxaudioelement_type;
|
||||||
|
@ -84,11 +84,9 @@ gst_osx_audio_element_class_init (GstOsxAudioElementInterface * klass)
|
||||||
static gboolean initialized = FALSE;
|
static gboolean initialized = FALSE;
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
|
|
||||||
initialized = TRUE;
|
initialized = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* default virtual functions */
|
/* default virtual functions */
|
||||||
klass->io_proc = NULL;
|
klass->io_proc = NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
* Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
||||||
* Copyright 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
* Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* to deal in the Software without restriction, including without limitation
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
@ -41,8 +41,9 @@
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*
|
*
|
||||||
* The development of this code was made possible due to the involvement of Pioneers of the * Inevitable, the creators of the Songbird Music player
|
* The development of this code was made possible due to the involvement of
|
||||||
*
|
* Pioneers of the Inevitable, the creators of the Songbird Music player
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __GST_OSX_AUDIO_ELEMENT_H__
|
#ifndef __GST_OSX_AUDIO_ELEMENT_H__
|
||||||
|
@ -50,22 +51,34 @@
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <CoreAudio/CoreAudio.h>
|
#include <CoreAudio/CoreAudio.h>
|
||||||
|
#include <AudioUnit/AudioUnit.h>
|
||||||
|
|
||||||
#define GST_OSX_AUDIO_ELEMENT_TYPE (gst_osx_audio_element_get_type())
|
G_BEGIN_DECLS
|
||||||
#define GST_OSX_AUDIO_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_OSX_AUDIO_ELEMENT_TYPE, GstOsxAudioElementInterface))
|
|
||||||
#define GST_IS_OSX_AUDIO_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_OSX_AUDIO_ELEMENT_TYPE))
|
#define GST_OSX_AUDIO_ELEMENT_TYPE \
|
||||||
#define GST_OSX_AUDIO_ELEMENT_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_OSX_AUDIO_ELEMENT_TYPE, GstOsxAudioElementInterface))
|
(gst_osx_audio_element_get_type())
|
||||||
|
#define GST_OSX_AUDIO_ELEMENT(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_OSX_AUDIO_ELEMENT_TYPE,GstOsxAudioElementInterface))
|
||||||
|
#define GST_IS_OSX_AUDIO_ELEMENT(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_OSX_AUDIO_ELEMENT_TYPE))
|
||||||
|
#define GST_OSX_AUDIO_ELEMENT_GET_INTERFACE(inst) \
|
||||||
|
(G_TYPE_INSTANCE_GET_INTERFACE((inst),GST_OSX_AUDIO_ELEMENT_TYPE,GstOsxAudioElementInterface))
|
||||||
|
|
||||||
typedef struct _GstOsxAudioElementInterface GstOsxAudioElementInterface;
|
typedef struct _GstOsxAudioElementInterface GstOsxAudioElementInterface;
|
||||||
|
|
||||||
struct _GstOsxAudioElementInterface {
|
struct _GstOsxAudioElementInterface
|
||||||
GTypeInterface parent;
|
{
|
||||||
|
GTypeInterface parent;
|
||||||
OSStatus (*io_proc) (AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData);
|
|
||||||
|
OSStatus (*io_proc) (void * userdata,
|
||||||
|
AudioUnitRenderActionFlags * ioActionFlags,
|
||||||
|
const AudioTimeStamp * inTimeStamp,
|
||||||
|
UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||||
|
AudioBufferList * bufferList);
|
||||||
};
|
};
|
||||||
|
|
||||||
GType gst_osx_audio_element_get_type (void);
|
GType gst_osx_audio_element_get_type (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif /* __GST_OSX_AUDIO_ELEMENT_H__ */
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
* Copyright (C) 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
||||||
* Copyright 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
* Copyright (C) 2007,2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* to deal in the Software without restriction, including without limitation
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
@ -41,8 +41,8 @@
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*
|
*
|
||||||
* The development of this code was made possible due to the involvement of
|
* The development of this code was made possible due to the involvement of
|
||||||
* Pioneers of the Inevitable, the creators of the Songbird Music player.
|
* Pioneers of the Inevitable, the creators of the Songbird Music player
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -67,8 +67,6 @@
|
||||||
#include <CoreAudio/CoreAudio.h>
|
#include <CoreAudio/CoreAudio.h>
|
||||||
#include <CoreAudio/AudioHardware.h>
|
#include <CoreAudio/AudioHardware.h>
|
||||||
#include "gstosxaudiosink.h"
|
#include "gstosxaudiosink.h"
|
||||||
#include "gstosxaudiosrc.h"
|
|
||||||
|
|
||||||
#include "gstosxaudioelement.h"
|
#include "gstosxaudioelement.h"
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (osx_audiosink_debug);
|
GST_DEBUG_CATEGORY_STATIC (osx_audiosink_debug);
|
||||||
|
@ -90,9 +88,12 @@ enum
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
ARG_0,
|
ARG_0,
|
||||||
ARG_DEVICE
|
ARG_DEVICE,
|
||||||
|
ARG_VOLUME
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define DEFAULT_VOLUME 1.0
|
||||||
|
|
||||||
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
|
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
GST_PAD_SINK,
|
GST_PAD_SINK,
|
||||||
GST_PAD_ALWAYS,
|
GST_PAD_ALWAYS,
|
||||||
|
@ -101,27 +102,28 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
"signed = (boolean) { TRUE }, "
|
"signed = (boolean) { TRUE }, "
|
||||||
"width = (int) 32, "
|
"width = (int) 32, "
|
||||||
"depth = (int) 32, "
|
"depth = (int) 32, "
|
||||||
"rate = (int) [1, MAX], " "channels = (int) [1, 2]")
|
"rate = (int) [1, MAX], " "channels = (int) [1, MAX]")
|
||||||
);
|
);
|
||||||
|
|
||||||
static void gst_osx_audio_sink_set_property (GObject * object, guint prop_id,
|
static void gst_osx_audio_sink_set_property (GObject * object, guint prop_id,
|
||||||
const GValue * value, GParamSpec * pspec);
|
const GValue * value, GParamSpec * pspec);
|
||||||
static void gst_osx_audio_sink_get_property (GObject * object, guint prop_id,
|
static void gst_osx_audio_sink_get_property (GObject * object, guint prop_id,
|
||||||
GValue * value, GParamSpec * pspec);
|
GValue * value, GParamSpec * pspec);
|
||||||
static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * sink);
|
|
||||||
|
|
||||||
static GstRingBuffer *gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink *
|
static GstRingBuffer *gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink *
|
||||||
sink);
|
sink);
|
||||||
static void gst_osx_audio_sink_osxelement_init (gpointer g_iface,
|
static void gst_osx_audio_sink_osxelement_init (gpointer g_iface,
|
||||||
gpointer iface_data);
|
gpointer iface_data);
|
||||||
OSStatus gst_osx_audio_sink_io_proc (AudioDeviceID inDevice,
|
|
||||||
const AudioTimeStamp * inNow, const AudioBufferList * inInputData,
|
|
||||||
const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData,
|
|
||||||
const AudioTimeStamp * inOutputTime, void *inClientData);
|
|
||||||
static void gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink);
|
static void gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink);
|
||||||
|
static void gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink);
|
||||||
|
|
||||||
|
static OSStatus gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf,
|
||||||
|
AudioUnitRenderActionFlags * ioActionFlags,
|
||||||
|
const AudioTimeStamp * inTimeStamp,
|
||||||
|
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_sink_osxelement_do_init (GType type)
|
gst_osx_audio_sink_do_init (GType type)
|
||||||
{
|
{
|
||||||
static const GInterfaceInfo osxelement_info = {
|
static const GInterfaceInfo osxelement_info = {
|
||||||
gst_osx_audio_sink_osxelement_init,
|
gst_osx_audio_sink_osxelement_init,
|
||||||
|
@ -137,8 +139,7 @@ gst_osx_audio_sink_osxelement_do_init (GType type)
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_BOILERPLATE_FULL (GstOsxAudioSink, gst_osx_audio_sink, GstBaseAudioSink,
|
GST_BOILERPLATE_FULL (GstOsxAudioSink, gst_osx_audio_sink, GstBaseAudioSink,
|
||||||
GST_TYPE_BASE_AUDIO_SINK, gst_osx_audio_sink_osxelement_do_init);
|
GST_TYPE_BASE_AUDIO_SINK, gst_osx_audio_sink_do_init);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_sink_base_init (gpointer g_class)
|
gst_osx_audio_sink_base_init (gpointer g_class)
|
||||||
|
@ -151,7 +152,6 @@ gst_osx_audio_sink_base_init (gpointer g_class)
|
||||||
gst_element_class_set_details (element_class, &gst_osx_audio_sink_details);
|
gst_element_class_set_details (element_class, &gst_osx_audio_sink_details);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize the plugin's class */
|
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
|
gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
|
||||||
{
|
{
|
||||||
|
@ -176,25 +176,21 @@ gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
|
||||||
g_param_spec_int ("device", "Device ID", "Device ID of output device",
|
g_param_spec_int ("device", "Device ID", "Device ID of output device",
|
||||||
0, G_MAXINT, 0, G_PARAM_READWRITE));
|
0, G_MAXINT, 0, G_PARAM_READWRITE));
|
||||||
|
|
||||||
gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_getcaps);
|
g_object_class_install_property (gobject_class, ARG_VOLUME,
|
||||||
|
g_param_spec_double ("volume", "Volume", "Volume of this stream",
|
||||||
|
0, 1.0, 1.0, G_PARAM_READWRITE));
|
||||||
|
|
||||||
gstbaseaudiosink_class->create_ringbuffer =
|
gstbaseaudiosink_class->create_ringbuffer =
|
||||||
GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer);
|
GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize the new element
|
|
||||||
* instantiate pads and add them to element
|
|
||||||
* set functions
|
|
||||||
* initialize structure
|
|
||||||
*/
|
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_sink_init (GstOsxAudioSink * sink, GstOsxAudioSinkClass * gclass)
|
gst_osx_audio_sink_init (GstOsxAudioSink * sink, GstOsxAudioSinkClass * gclass)
|
||||||
{
|
{
|
||||||
/* GstElementClass *klass = GST_ELEMENT_GET_CLASS (sink); */
|
|
||||||
GST_DEBUG ("Initialising object");
|
GST_DEBUG ("Initialising object");
|
||||||
|
|
||||||
sink->device_id = kAudioDeviceUnknown;
|
sink->device_id = kAudioDeviceUnknown;
|
||||||
sink->stream_id = kAudioStreamUnknown;
|
sink->volume = DEFAULT_VOLUME;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -207,6 +203,10 @@ gst_osx_audio_sink_set_property (GObject * object, guint prop_id,
|
||||||
case ARG_DEVICE:
|
case ARG_DEVICE:
|
||||||
sink->device_id = g_value_get_int (value);
|
sink->device_id = g_value_get_int (value);
|
||||||
break;
|
break;
|
||||||
|
case ARG_VOLUME:
|
||||||
|
sink->volume = g_value_get_double (value);
|
||||||
|
gst_osx_audio_sink_set_volume (sink);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -222,109 +222,15 @@ gst_osx_audio_sink_get_property (GObject * object, guint prop_id,
|
||||||
case ARG_DEVICE:
|
case ARG_DEVICE:
|
||||||
g_value_set_int (value, sink->device_id);
|
g_value_set_int (value, sink->device_id);
|
||||||
break;
|
break;
|
||||||
|
case ARG_VOLUME:
|
||||||
|
g_value_set_double (value, sink->volume);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* GstElement vmethod implementations */
|
|
||||||
|
|
||||||
/* GstBaseSink vmethod implementations */
|
|
||||||
static GstCaps *
|
|
||||||
gst_osx_audio_sink_getcaps (GstBaseSink * sink)
|
|
||||||
{
|
|
||||||
GstCaps *caps = NULL;
|
|
||||||
GstOsxAudioSink *osxsink;
|
|
||||||
OSStatus status;
|
|
||||||
AudioValueRange *rates = NULL;
|
|
||||||
UInt32 propertySize;
|
|
||||||
int i;
|
|
||||||
gboolean foundFixedRate = FALSE;
|
|
||||||
GstStructure *structure;
|
|
||||||
GValue rate_v = { 0 };
|
|
||||||
GValue rates_v = { 0 };
|
|
||||||
|
|
||||||
osxsink = GST_OSX_AUDIO_SINK (sink);
|
|
||||||
|
|
||||||
gst_osx_audio_sink_select_device (osxsink);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Using device_id %d", (int) osxsink->device_id);
|
|
||||||
|
|
||||||
status = AudioDeviceGetPropertyInfo (osxsink->device_id, 0, /* Master channel */
|
|
||||||
FALSE, /* isInput */
|
|
||||||
kAudioDevicePropertyAvailableNominalSampleRates, &propertySize, NULL);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (osxsink, "Failed to get sample rates size: %ld",
|
|
||||||
status);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Allocating %d bytes for sizes",
|
|
||||||
(int) propertySize);
|
|
||||||
rates = g_malloc (propertySize);
|
|
||||||
|
|
||||||
status = AudioDeviceGetProperty (osxsink->device_id, 0, /* Master channel */
|
|
||||||
FALSE, /* isInput */
|
|
||||||
kAudioDevicePropertyAvailableNominalSampleRates, &propertySize, rates);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (osxsink, "Failed to get sample rates: %ld", status);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Used %d bytes for sizes", (int) propertySize);
|
|
||||||
|
|
||||||
if (propertySize < sizeof (AudioValueRange)) {
|
|
||||||
GST_WARNING_OBJECT (osxsink, "Zero sample rates available");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create base caps object, then modify to suit. */
|
|
||||||
caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
|
|
||||||
(sink)));
|
|
||||||
structure = gst_caps_get_structure (caps, 0);
|
|
||||||
|
|
||||||
GST_DEBUG
|
|
||||||
("Getting available sample rates: Status: %ld number of ranges: %lu",
|
|
||||||
status, propertySize / sizeof (AudioValueRange));
|
|
||||||
|
|
||||||
g_value_init (&rates_v, GST_TYPE_LIST);
|
|
||||||
g_value_init (&rate_v, G_TYPE_INT);
|
|
||||||
|
|
||||||
for (i = 0; i < propertySize / sizeof (AudioValueRange); i++) {
|
|
||||||
GST_LOG_OBJECT (osxsink, "Range from %f to %f", rates[i].mMinimum,
|
|
||||||
rates[i].mMaximum);
|
|
||||||
if (rates[i].mMinimum == rates[i].mMaximum) {
|
|
||||||
/* For now, we only support these in this form. If there are none
|
|
||||||
* in this form, we use the first (only) as a range. */
|
|
||||||
foundFixedRate = TRUE;
|
|
||||||
|
|
||||||
g_value_set_int (&rate_v, rates[i].mMinimum);
|
|
||||||
gst_value_list_append_value (&rates_v, &rate_v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_value_unset (&rate_v);
|
|
||||||
|
|
||||||
if (foundFixedRate) {
|
|
||||||
gst_structure_set_value (structure, "rate", &rates_v);
|
|
||||||
} else {
|
|
||||||
gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE,
|
|
||||||
rates[0].mMinimum, rates[0].mMaximum, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_value_unset (&rates_v);
|
|
||||||
|
|
||||||
done:
|
|
||||||
if (rates)
|
|
||||||
g_free (rates);
|
|
||||||
|
|
||||||
return caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* GstBaseAudioSink vmethod implementations */
|
|
||||||
static GstRingBuffer *
|
static GstRingBuffer *
|
||||||
gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink)
|
gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink)
|
||||||
{
|
{
|
||||||
|
@ -340,35 +246,57 @@ gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink)
|
||||||
GST_DEBUG ("osx sink 0x%p element 0x%p ioproc 0x%p", osxsink,
|
GST_DEBUG ("osx sink 0x%p element 0x%p ioproc 0x%p", osxsink,
|
||||||
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink),
|
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink),
|
||||||
(void *) gst_osx_audio_sink_io_proc);
|
(void *) gst_osx_audio_sink_io_proc);
|
||||||
|
|
||||||
|
gst_osx_audio_sink_set_volume (osxsink);
|
||||||
|
|
||||||
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink);
|
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink);
|
||||||
ringbuffer->device_id = osxsink->device_id;
|
ringbuffer->device_id = osxsink->device_id;
|
||||||
ringbuffer->stream_id = osxsink->stream_id;
|
|
||||||
|
|
||||||
return GST_RING_BUFFER (ringbuffer);
|
return GST_RING_BUFFER (ringbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
OSStatus
|
/* HALOutput AudioUnit will request fairly arbitrarily-sized chunks of data,
|
||||||
gst_osx_audio_sink_io_proc (AudioDeviceID inDevice,
|
* not of a fixed size. So, we keep track of where in the current ringbuffer
|
||||||
const AudioTimeStamp * inNow, const AudioBufferList * inInputData,
|
* segment we are, and only advance the segment once we've read the whole
|
||||||
const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData,
|
* thing */
|
||||||
const AudioTimeStamp * inOutputTime, void *inClientData)
|
static OSStatus
|
||||||
|
gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf,
|
||||||
|
AudioUnitRenderActionFlags * ioActionFlags,
|
||||||
|
const AudioTimeStamp * inTimeStamp,
|
||||||
|
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList)
|
||||||
{
|
{
|
||||||
GstOsxRingBuffer *buf = GST_OSX_RING_BUFFER (inClientData);
|
|
||||||
|
|
||||||
guint8 *readptr;
|
guint8 *readptr;
|
||||||
gint readseg;
|
gint readseg;
|
||||||
gint len;
|
gint len;
|
||||||
|
gint remaining = bufferList->mBuffers[0].mDataByteSize;
|
||||||
|
gint offset = 0;
|
||||||
|
|
||||||
if (gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), &readseg, &readptr,
|
while (remaining) {
|
||||||
&len)) {
|
if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf),
|
||||||
outOutputData->mBuffers[0].mDataByteSize = len;
|
&readseg, &readptr, &len))
|
||||||
memcpy ((char *) outOutputData->mBuffers[0].mData, readptr, len);
|
return 0;
|
||||||
|
|
||||||
/* clear written samples */
|
len -= buf->segoffset;
|
||||||
gst_ring_buffer_clear (GST_RING_BUFFER (buf), readseg);
|
|
||||||
|
|
||||||
/* we wrote one segment */
|
if (len > remaining)
|
||||||
gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1);
|
len = remaining;
|
||||||
|
|
||||||
|
memcpy ((char *) bufferList->mBuffers[0].mData + offset,
|
||||||
|
readptr + buf->segoffset, len);
|
||||||
|
|
||||||
|
buf->segoffset += len;
|
||||||
|
offset += len;
|
||||||
|
remaining -= len;
|
||||||
|
|
||||||
|
if ((gint) buf->segoffset == GST_RING_BUFFER (buf)->spec.segsize) {
|
||||||
|
/* clear written samples */
|
||||||
|
gst_ring_buffer_clear (GST_RING_BUFFER (buf), readseg);
|
||||||
|
|
||||||
|
/* we wrote one segment */
|
||||||
|
gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1);
|
||||||
|
|
||||||
|
buf->segoffset = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -378,9 +306,18 @@ gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data)
|
||||||
{
|
{
|
||||||
GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface;
|
GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface;
|
||||||
|
|
||||||
iface->io_proc = gst_osx_audio_sink_io_proc;
|
iface->io_proc = (AURenderCallback) gst_osx_audio_sink_io_proc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink)
|
||||||
|
{
|
||||||
|
if (!sink->audiounit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AudioUnitSetParameter (sink->audiounit, kHALOutputParam_Volume,
|
||||||
|
kAudioUnitScope_Global, 0, (float) sink->volume, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink)
|
gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink)
|
||||||
|
@ -389,67 +326,27 @@ gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink)
|
||||||
UInt32 propertySize;
|
UInt32 propertySize;
|
||||||
|
|
||||||
if (osxsink->device_id == kAudioDeviceUnknown) {
|
if (osxsink->device_id == kAudioDeviceUnknown) {
|
||||||
|
/* If no specific device has been selected by the user, then pick the
|
||||||
|
* default device */
|
||||||
GST_DEBUG_OBJECT (osxsink, "Selecting device for OSXAudioSink");
|
GST_DEBUG_OBJECT (osxsink, "Selecting device for OSXAudioSink");
|
||||||
propertySize = sizeof (osxsink->device_id);
|
propertySize = sizeof (osxsink->device_id);
|
||||||
status =
|
status =
|
||||||
AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice,
|
AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice,
|
||||||
&propertySize, &osxsink->device_id);
|
&propertySize, &osxsink->device_id);
|
||||||
|
|
||||||
if (status)
|
if (status) {
|
||||||
GST_WARNING_OBJECT (osxsink,
|
GST_WARNING_OBJECT (osxsink,
|
||||||
"AudioHardwareGetProperty returned %d", (int) status);
|
"AudioHardwareGetProperty returned %d", (int) status);
|
||||||
else
|
} else {
|
||||||
GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty returned 0");
|
GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty returned 0");
|
||||||
|
}
|
||||||
|
|
||||||
if (osxsink->device_id == kAudioDeviceUnknown)
|
if (osxsink->device_id == kAudioDeviceUnknown) {
|
||||||
GST_WARNING_OBJECT (osxsink,
|
GST_WARNING_OBJECT (osxsink,
|
||||||
"AudioHardwareGetProperty: device_id is kAudioDeviceUnknown");
|
"AudioHardwareGetProperty: device_id is kAudioDeviceUnknown");
|
||||||
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty: device_id is %lu",
|
GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty: device_id is %lu",
|
||||||
(long) osxsink->device_id);
|
(long) osxsink->device_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (osxsink->stream_id == kAudioStreamUnknown) {
|
|
||||||
AudioStreamID *streams;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Getting streamid");
|
|
||||||
status = AudioDeviceGetPropertyInfo (osxsink->device_id, 0, /* Master channel */
|
|
||||||
FALSE, /* isInput */
|
|
||||||
kAudioDevicePropertyStreams, &propertySize, NULL);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (osxsink,
|
|
||||||
"AudioDeviceGetProperty returned %d", (int) status);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink,
|
|
||||||
"Getting available streamids from %d (%d bytes)",
|
|
||||||
(int) (propertySize / sizeof (AudioStreamID)),
|
|
||||||
(unsigned int) propertySize);
|
|
||||||
streams = g_malloc (propertySize);
|
|
||||||
status = AudioDeviceGetProperty (osxsink->device_id, 0, /* Master channel */
|
|
||||||
FALSE, /* isInput */
|
|
||||||
kAudioDevicePropertyStreams, &propertySize, streams);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (osxsink,
|
|
||||||
"AudioDeviceGetProperty returned %d", (int) status);
|
|
||||||
g_free (streams);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Getting streamid from %d (%d bytes)",
|
|
||||||
(int) (propertySize / sizeof (AudioStreamID)),
|
|
||||||
(unsigned int) propertySize);
|
|
||||||
|
|
||||||
if (propertySize >= sizeof (AudioStreamID)) {
|
|
||||||
osxsink->stream_id = streams[0];
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Selected stream %d of %d: %d", 0,
|
|
||||||
(int) (propertySize / sizeof (AudioStreamID)),
|
|
||||||
(int) osxsink->stream_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (streams);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright 2005-2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
* Copyright (C) 2005-2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
||||||
* Copyright 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
* Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* to deal in the Software without restriction, including without limitation
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
@ -40,11 +40,10 @@
|
||||||
* License along with this library; if not, write to the
|
* License along with this library; if not, write to the
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*
|
|
||||||
* The development of this code was made possible due to the involvement of Pioneers of
|
|
||||||
* the Inevitable, the creators of the Songbird Music player
|
|
||||||
*
|
*
|
||||||
|
* The development of this code was made possible due to the involvement of
|
||||||
|
* Pioneers of the Inevitable, the creators of the Songbird Music player
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __GST_OSXAUDIOSINK_H__
|
#ifndef __GST_OSXAUDIOSINK_H__
|
||||||
|
@ -56,7 +55,6 @@
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
/* #defines don't like whitespacey bits */
|
|
||||||
#define GST_TYPE_OSX_AUDIO_SINK \
|
#define GST_TYPE_OSX_AUDIO_SINK \
|
||||||
(gst_osx_audio_sink_get_type())
|
(gst_osx_audio_sink_get_type())
|
||||||
#define GST_OSX_AUDIO_SINK(obj) \
|
#define GST_OSX_AUDIO_SINK(obj) \
|
||||||
|
@ -64,7 +62,7 @@ G_BEGIN_DECLS
|
||||||
#define GST_OSX_AUDIO_SINK_CLASS(klass) \
|
#define GST_OSX_AUDIO_SINK_CLASS(klass) \
|
||||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SINK,GstOsxAudioSinkClass))
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SINK,GstOsxAudioSinkClass))
|
||||||
|
|
||||||
typedef struct _GstOsxAudioSink GstOsxAudioSink;
|
typedef struct _GstOsxAudioSink GstOsxAudioSink;
|
||||||
typedef struct _GstOsxAudioSinkClass GstOsxAudioSinkClass;
|
typedef struct _GstOsxAudioSinkClass GstOsxAudioSinkClass;
|
||||||
|
|
||||||
struct _GstOsxAudioSink
|
struct _GstOsxAudioSink
|
||||||
|
@ -72,7 +70,8 @@ struct _GstOsxAudioSink
|
||||||
GstBaseAudioSink sink;
|
GstBaseAudioSink sink;
|
||||||
|
|
||||||
AudioDeviceID device_id;
|
AudioDeviceID device_id;
|
||||||
AudioStreamID stream_id;
|
AudioUnit audiounit;
|
||||||
|
double volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstOsxAudioSinkClass
|
struct _GstOsxAudioSinkClass
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
* Copyright (C) 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
||||||
* 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
* Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* to deal in the Software without restriction, including without limitation
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
@ -61,6 +61,7 @@
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <CoreAudio/CoreAudio.h>
|
#include <CoreAudio/CoreAudio.h>
|
||||||
|
#include <CoreAudio/AudioHardware.h>
|
||||||
#include "gstosxaudiosrc.h"
|
#include "gstosxaudiosrc.h"
|
||||||
#include "gstosxaudioelement.h"
|
#include "gstosxaudioelement.h"
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
"signed = (boolean) { TRUE }, "
|
"signed = (boolean) { TRUE }, "
|
||||||
"width = (int) 32, "
|
"width = (int) 32, "
|
||||||
"depth = (int) 32, "
|
"depth = (int) 32, "
|
||||||
"rate = (int) [1, MAX], " "channels = (int) [1, 2]")
|
"rate = (int) [1, MAX], " "channels = (int) [1, MAX]")
|
||||||
);
|
);
|
||||||
|
|
||||||
static void gst_osx_audio_src_set_property (GObject * object, guint prop_id,
|
static void gst_osx_audio_src_set_property (GObject * object, guint prop_id,
|
||||||
|
@ -102,19 +103,20 @@ static void gst_osx_audio_src_set_property (GObject * object, guint prop_id,
|
||||||
static void gst_osx_audio_src_get_property (GObject * object, guint prop_id,
|
static void gst_osx_audio_src_get_property (GObject * object, guint prop_id,
|
||||||
GValue * value, GParamSpec * pspec);
|
GValue * value, GParamSpec * pspec);
|
||||||
|
|
||||||
|
static GstCaps *gst_osx_audio_src_get_caps (GstBaseSrc * src);
|
||||||
|
|
||||||
static GstRingBuffer *gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc *
|
static GstRingBuffer *gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc *
|
||||||
src);
|
src);
|
||||||
static void gst_osx_audio_src_osxelement_init (gpointer g_iface,
|
static void gst_osx_audio_src_osxelement_init (gpointer g_iface,
|
||||||
gpointer iface_data);
|
gpointer iface_data);
|
||||||
OSStatus gst_osx_audio_src_io_proc (AudioDeviceID inDevice,
|
static OSStatus gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
|
||||||
const AudioTimeStamp * inNow, const AudioBufferList * inInputData,
|
AudioUnitRenderActionFlags * ioActionFlags,
|
||||||
const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData,
|
const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber,
|
||||||
const AudioTimeStamp * inOutputTime, void *inClientData);
|
UInt32 inNumberFrames, AudioBufferList * bufferList);
|
||||||
static void gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc);
|
static void gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_src_osxelement_do_init (GType type)
|
gst_osx_audio_src_do_init (GType type)
|
||||||
{
|
{
|
||||||
static const GInterfaceInfo osxelement_info = {
|
static const GInterfaceInfo osxelement_info = {
|
||||||
gst_osx_audio_src_osxelement_init,
|
gst_osx_audio_src_osxelement_init,
|
||||||
|
@ -130,8 +132,7 @@ gst_osx_audio_src_osxelement_do_init (GType type)
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_BOILERPLATE_FULL (GstOsxAudioSrc, gst_osx_audio_src, GstBaseAudioSrc,
|
GST_BOILERPLATE_FULL (GstOsxAudioSrc, gst_osx_audio_src, GstBaseAudioSrc,
|
||||||
GST_TYPE_BASE_AUDIO_SRC, gst_osx_audio_src_osxelement_do_init);
|
GST_TYPE_BASE_AUDIO_SRC, gst_osx_audio_src_do_init);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_src_base_init (gpointer g_class)
|
gst_osx_audio_src_base_init (gpointer g_class)
|
||||||
|
@ -144,16 +145,17 @@ gst_osx_audio_src_base_init (gpointer g_class)
|
||||||
gst_element_class_set_details (element_class, &gst_osx_audio_src_details);
|
gst_element_class_set_details (element_class, &gst_osx_audio_src_details);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize the plugin's class */
|
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_src_class_init (GstOsxAudioSrcClass * klass)
|
gst_osx_audio_src_class_init (GstOsxAudioSrcClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class;
|
GObjectClass *gobject_class;
|
||||||
GstElementClass *gstelement_class;
|
GstElementClass *gstelement_class;
|
||||||
|
GstBaseSrcClass *gstbasesrc_class;
|
||||||
GstBaseAudioSrcClass *gstbaseaudiosrc_class;
|
GstBaseAudioSrcClass *gstbaseaudiosrc_class;
|
||||||
|
|
||||||
gobject_class = (GObjectClass *) klass;
|
gobject_class = (GObjectClass *) klass;
|
||||||
gstelement_class = (GstElementClass *) klass;
|
gstelement_class = (GstElementClass *) klass;
|
||||||
|
gstbasesrc_class = (GstBaseSrcClass *) klass;
|
||||||
gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
|
gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
|
||||||
|
|
||||||
parent_class = g_type_class_peek_parent (klass);
|
parent_class = g_type_class_peek_parent (klass);
|
||||||
|
@ -163,28 +165,23 @@ gst_osx_audio_src_class_init (GstOsxAudioSrcClass * klass)
|
||||||
gobject_class->get_property =
|
gobject_class->get_property =
|
||||||
GST_DEBUG_FUNCPTR (gst_osx_audio_src_get_property);
|
GST_DEBUG_FUNCPTR (gst_osx_audio_src_get_property);
|
||||||
|
|
||||||
|
gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_src_get_caps);
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, ARG_DEVICE,
|
g_object_class_install_property (gobject_class, ARG_DEVICE,
|
||||||
g_param_spec_int ("device", "Device ID", "Device ID of input device",
|
g_param_spec_int ("device", "Device ID", "Device ID of input device",
|
||||||
0, G_MAXINT, 0, G_PARAM_READWRITE));
|
0, G_MAXINT, 0, G_PARAM_READWRITE));
|
||||||
|
|
||||||
gstbaseaudiosrc_class->create_ringbuffer =
|
gstbaseaudiosrc_class->create_ringbuffer =
|
||||||
GST_DEBUG_FUNCPTR (gst_osx_audio_src_create_ringbuffer);
|
GST_DEBUG_FUNCPTR (gst_osx_audio_src_create_ringbuffer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize the new element
|
|
||||||
* instantiate pads and add them to element
|
|
||||||
* set functions
|
|
||||||
* initialize structure
|
|
||||||
*/
|
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_src_init (GstOsxAudioSrc * src, GstOsxAudioSrcClass * gclass)
|
gst_osx_audio_src_init (GstOsxAudioSrc * src, GstOsxAudioSrcClass * gclass)
|
||||||
{
|
{
|
||||||
/* GstElementClass *klass = GST_ELEMENT_GET_CLASS (src); */
|
|
||||||
gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
|
gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
|
||||||
|
|
||||||
src->device_id = kAudioDeviceUnknown;
|
src->device_id = kAudioDeviceUnknown;
|
||||||
src->stream_id = kAudioStreamUnknown;
|
src->deviceChannels = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -219,10 +216,43 @@ gst_osx_audio_src_get_property (GObject * object, guint prop_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* GstElement vmethod implementations */
|
static GstCaps *
|
||||||
|
gst_osx_audio_src_get_caps (GstBaseSrc * src)
|
||||||
|
{
|
||||||
|
GstElementClass *gstelement_class;
|
||||||
|
GstOsxAudioSrc *osxsrc;
|
||||||
|
GstPadTemplate *pad_template;
|
||||||
|
GstCaps *caps;
|
||||||
|
GstStructure *structure;
|
||||||
|
gint min, max;
|
||||||
|
|
||||||
|
gstelement_class = GST_ELEMENT_GET_CLASS (src);
|
||||||
|
osxsrc = GST_OSX_AUDIO_SRC (src);
|
||||||
|
|
||||||
|
if (osxsrc->deviceChannels == -1) {
|
||||||
|
/* -1 means we don't know the number of channels yet. for now, return
|
||||||
|
* template caps.
|
||||||
|
*/
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
max = osxsrc->deviceChannels;
|
||||||
|
if (max < 1)
|
||||||
|
max = 1; /* 0 channels means 1 channel? */
|
||||||
|
|
||||||
|
min = MIN (1, max);
|
||||||
|
|
||||||
|
pad_template = gst_element_class_get_pad_template (gstelement_class, "src");
|
||||||
|
g_return_val_if_fail (pad_template != NULL, NULL);
|
||||||
|
|
||||||
|
caps = gst_caps_copy (gst_pad_template_get_caps (pad_template));
|
||||||
|
|
||||||
|
structure = gst_caps_get_structure (caps, 0);
|
||||||
|
gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE, min, max, NULL);
|
||||||
|
|
||||||
|
return caps;
|
||||||
|
}
|
||||||
|
|
||||||
/* GstBaseAudioSrc vmethod implementations */
|
|
||||||
static GstRingBuffer *
|
static GstRingBuffer *
|
||||||
gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
|
gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
|
||||||
{
|
{
|
||||||
|
@ -235,42 +265,63 @@ gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
|
||||||
|
|
||||||
GST_DEBUG ("Creating ringbuffer");
|
GST_DEBUG ("Creating ringbuffer");
|
||||||
ringbuffer = g_object_new (GST_TYPE_OSX_RING_BUFFER, NULL);
|
ringbuffer = g_object_new (GST_TYPE_OSX_RING_BUFFER, NULL);
|
||||||
|
|
||||||
/* change the device to the Default Input Device */
|
|
||||||
GST_DEBUG ("osx src 0x%p element 0x%p ioproc 0x%p", osxsrc,
|
GST_DEBUG ("osx src 0x%p element 0x%p ioproc 0x%p", osxsrc,
|
||||||
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc),
|
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc),
|
||||||
(void *) gst_osx_audio_src_io_proc);
|
(void *) gst_osx_audio_src_io_proc);
|
||||||
|
|
||||||
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc);
|
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc);
|
||||||
|
ringbuffer->is_src = TRUE;
|
||||||
ringbuffer->device_id = osxsrc->device_id;
|
ringbuffer->device_id = osxsrc->device_id;
|
||||||
ringbuffer->stream_id = osxsrc->stream_id;
|
|
||||||
|
|
||||||
return GST_RING_BUFFER (ringbuffer);
|
return GST_RING_BUFFER (ringbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
OSStatus
|
static OSStatus
|
||||||
gst_osx_audio_src_io_proc (AudioDeviceID inDevice, const AudioTimeStamp * inNow,
|
gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
|
||||||
const AudioBufferList * inInputData, const AudioTimeStamp * inInputTime,
|
AudioUnitRenderActionFlags * ioActionFlags,
|
||||||
AudioBufferList * outOutputData, const AudioTimeStamp * inOutputTime,
|
const AudioTimeStamp * inTimeStamp,
|
||||||
void *inClientData)
|
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList)
|
||||||
{
|
{
|
||||||
GstOsxRingBuffer *buf = GST_OSX_RING_BUFFER (inClientData);
|
OSStatus status;
|
||||||
|
|
||||||
guint8 *writeptr;
|
guint8 *writeptr;
|
||||||
gint writeseg;
|
gint writeseg;
|
||||||
gint len;
|
gint len;
|
||||||
gint bytesToCopy;
|
gint remaining;
|
||||||
|
gint offset = 0;
|
||||||
|
|
||||||
if (gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), &writeseg, &writeptr,
|
status = AudioUnitRender (buf->audiounit, ioActionFlags, inTimeStamp,
|
||||||
&len)) {
|
inBusNumber, inNumberFrames, buf->recBufferList);
|
||||||
bytesToCopy = inInputData->mBuffers[0].mDataByteSize;
|
|
||||||
memcpy (writeptr, (char *) inInputData->mBuffers[0].mData, bytesToCopy);
|
|
||||||
|
|
||||||
/* clear written samples */
|
if (status) {
|
||||||
/*gst_ring_buffer_clear (GST_RING_BUFFER(buf), writeseg); */
|
GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/* we wrote one segment */
|
remaining = buf->recBufferList->mBuffers[0].mDataByteSize;
|
||||||
gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1);
|
|
||||||
|
while (remaining) {
|
||||||
|
if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf),
|
||||||
|
&writeseg, &writeptr, &len))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len -= buf->segoffset;
|
||||||
|
|
||||||
|
if (len > remaining)
|
||||||
|
len = remaining;
|
||||||
|
|
||||||
|
memcpy (writeptr + buf->segoffset,
|
||||||
|
(char *) buf->recBufferList->mBuffers[0].mData + offset, len);
|
||||||
|
|
||||||
|
buf->segoffset += len;
|
||||||
|
offset += len;
|
||||||
|
remaining -= len;
|
||||||
|
|
||||||
|
if ((gint) buf->segoffset == GST_RING_BUFFER (buf)->spec.segsize) {
|
||||||
|
/* we wrote one segment */
|
||||||
|
gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1);
|
||||||
|
|
||||||
|
buf->segoffset = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -280,7 +331,7 @@ gst_osx_audio_src_osxelement_init (gpointer g_iface, gpointer iface_data)
|
||||||
{
|
{
|
||||||
GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface;
|
GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface;
|
||||||
|
|
||||||
iface->io_proc = gst_osx_audio_src_io_proc;
|
iface->io_proc = (AURenderCallback) gst_osx_audio_src_io_proc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -290,66 +341,26 @@ gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc)
|
||||||
UInt32 propertySize;
|
UInt32 propertySize;
|
||||||
|
|
||||||
if (osxsrc->device_id == kAudioDeviceUnknown) {
|
if (osxsrc->device_id == kAudioDeviceUnknown) {
|
||||||
|
/* If no specific device has been selected by the user, then pick the
|
||||||
|
* default device */
|
||||||
GST_DEBUG_OBJECT (osxsrc, "Selecting device for OSXAudioSrc");
|
GST_DEBUG_OBJECT (osxsrc, "Selecting device for OSXAudioSrc");
|
||||||
propertySize = sizeof (osxsrc->device_id);
|
propertySize = sizeof (osxsrc->device_id);
|
||||||
status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice,
|
status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice,
|
||||||
&propertySize, &osxsrc->device_id);
|
&propertySize, &osxsrc->device_id);
|
||||||
|
|
||||||
if (status)
|
if (status) {
|
||||||
GST_WARNING_OBJECT (osxsrc,
|
GST_WARNING_OBJECT (osxsrc,
|
||||||
"AudioHardwareGetProperty returned %d", (int) status);
|
"AudioHardwareGetProperty returned %d", (int) status);
|
||||||
else
|
} else {
|
||||||
GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty returned 0");
|
GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty returned 0");
|
||||||
|
}
|
||||||
|
|
||||||
if (osxsrc->device_id == kAudioDeviceUnknown)
|
if (osxsrc->device_id == kAudioDeviceUnknown) {
|
||||||
GST_WARNING_OBJECT (osxsrc,
|
GST_WARNING_OBJECT (osxsrc,
|
||||||
"AudioHardwareGetProperty: device_id is kAudioDeviceUnknown");
|
"AudioHardwareGetProperty: device_id is kAudioDeviceUnknown");
|
||||||
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty: device_id is %lu",
|
GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty: device_id is %lu",
|
||||||
(long) osxsrc->device_id);
|
(long) osxsrc->device_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (osxsrc->stream_id == kAudioStreamUnknown) {
|
|
||||||
AudioStreamID *streams;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsrc, "Getting streamid");
|
|
||||||
status = AudioDeviceGetPropertyInfo (osxsrc->device_id, 0, /* Master channel */
|
|
||||||
FALSE, /* isInput */
|
|
||||||
kAudioDevicePropertyStreams, &propertySize, NULL);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (osxsrc,
|
|
||||||
"AudioDeviceGetProperty returned %d", (int) status);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsrc,
|
|
||||||
"Getting available streamids from %d (%d bytes)",
|
|
||||||
(int) (propertySize / sizeof (AudioStreamID)),
|
|
||||||
(unsigned int) propertySize);
|
|
||||||
streams = g_malloc (propertySize);
|
|
||||||
status = AudioDeviceGetProperty (osxsrc->device_id, 0, /* Master channel */
|
|
||||||
FALSE, /* isInput */
|
|
||||||
kAudioDevicePropertyStreams, &propertySize, streams);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (osxsrc,
|
|
||||||
"AudioDeviceGetProperty returned %d", (int) status);
|
|
||||||
g_free (streams);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsrc, "Getting streamid from %d (%d bytes)",
|
|
||||||
(int) (propertySize / sizeof (AudioStreamID)),
|
|
||||||
(unsigned int) propertySize);
|
|
||||||
|
|
||||||
if (propertySize >= sizeof (AudioStreamID)) {
|
|
||||||
osxsrc->stream_id = streams[0];
|
|
||||||
GST_DEBUG_OBJECT (osxsrc, "Selected stream %d of %d: %d", 0,
|
|
||||||
(int) (propertySize / sizeof (AudioStreamID)),
|
|
||||||
(int) osxsrc->stream_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (streams);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright 2005-2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
* Copyright (C) 2005-2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* to deal in the Software without restriction, including without limitation
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
@ -50,7 +50,6 @@
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
/* #defines don't like whitespacey bits */
|
|
||||||
#define GST_TYPE_OSX_AUDIO_SRC \
|
#define GST_TYPE_OSX_AUDIO_SRC \
|
||||||
(gst_osx_audio_src_get_type())
|
(gst_osx_audio_src_get_type())
|
||||||
#define GST_OSX_AUDIO_SRC(obj) \
|
#define GST_OSX_AUDIO_SRC(obj) \
|
||||||
|
@ -58,7 +57,7 @@ G_BEGIN_DECLS
|
||||||
#define GST_OSX_AUDIO_SRC_CLASS(klass) \
|
#define GST_OSX_AUDIO_SRC_CLASS(klass) \
|
||||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SRC,GstOsxAudioSrcClass))
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SRC,GstOsxAudioSrcClass))
|
||||||
|
|
||||||
typedef struct _GstOsxAudioSrc GstOsxAudioSrc;
|
typedef struct _GstOsxAudioSrc GstOsxAudioSrc;
|
||||||
typedef struct _GstOsxAudioSrcClass GstOsxAudioSrcClass;
|
typedef struct _GstOsxAudioSrcClass GstOsxAudioSrcClass;
|
||||||
|
|
||||||
struct _GstOsxAudioSrc
|
struct _GstOsxAudioSrc
|
||||||
|
@ -66,7 +65,9 @@ struct _GstOsxAudioSrc
|
||||||
GstBaseAudioSrc src;
|
GstBaseAudioSrc src;
|
||||||
|
|
||||||
AudioDeviceID device_id;
|
AudioDeviceID device_id;
|
||||||
AudioStreamID stream_id;
|
|
||||||
|
/* actual number of channels reported by input device */
|
||||||
|
int deviceChannels;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstOsxAudioSrcClass
|
struct _GstOsxAudioSrcClass
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
* Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
||||||
*
|
* Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
||||||
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* to deal in the Software without restriction, including without limitation
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
@ -43,14 +44,14 @@
|
||||||
|
|
||||||
#include <CoreAudio/CoreAudio.h>
|
#include <CoreAudio/CoreAudio.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/audio/multichannel.h>
|
||||||
#include "gstosxringbuffer.h"
|
#include "gstosxringbuffer.h"
|
||||||
|
#include "gstosxaudiosink.h"
|
||||||
|
#include "gstosxaudiosrc.h"
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
|
GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
|
||||||
#define GST_CAT_DEFAULT osx_audio_debug
|
#define GST_CAT_DEFAULT osx_audio_debug
|
||||||
|
|
||||||
static void gst_osx_ring_buffer_class_init (GstOsxRingBufferClass * klass);
|
|
||||||
static void gst_osx_ring_buffer_init (GstOsxRingBuffer * ringbuffer,
|
|
||||||
GstOsxRingBufferClass * g_class);
|
|
||||||
static void gst_osx_ring_buffer_dispose (GObject * object);
|
static void gst_osx_ring_buffer_dispose (GObject * object);
|
||||||
static void gst_osx_ring_buffer_finalize (GObject * object);
|
static void gst_osx_ring_buffer_finalize (GObject * object);
|
||||||
static gboolean gst_osx_ring_buffer_open_device (GstRingBuffer * buf);
|
static gboolean gst_osx_ring_buffer_open_device (GstRingBuffer * buf);
|
||||||
|
@ -60,43 +61,34 @@ static gboolean gst_osx_ring_buffer_acquire (GstRingBuffer * buf,
|
||||||
GstRingBufferSpec * spec);
|
GstRingBufferSpec * spec);
|
||||||
static gboolean gst_osx_ring_buffer_release (GstRingBuffer * buf);
|
static gboolean gst_osx_ring_buffer_release (GstRingBuffer * buf);
|
||||||
|
|
||||||
/* static gboolean gst_osx_ring_buffer_device_is_acquired (GstRingBuffer * buf); */
|
|
||||||
|
|
||||||
static gboolean gst_osx_ring_buffer_start (GstRingBuffer * buf);
|
static gboolean gst_osx_ring_buffer_start (GstRingBuffer * buf);
|
||||||
static gboolean gst_osx_ring_buffer_pause (GstRingBuffer * buf);
|
static gboolean gst_osx_ring_buffer_pause (GstRingBuffer * buf);
|
||||||
static gboolean gst_osx_ring_buffer_stop (GstRingBuffer * buf);
|
static gboolean gst_osx_ring_buffer_stop (GstRingBuffer * buf);
|
||||||
static guint gst_osx_ring_buffer_delay (GstRingBuffer * buf);
|
static guint gst_osx_ring_buffer_delay (GstRingBuffer * buf);
|
||||||
static GstRingBufferClass *ring_parent_class = NULL;
|
static GstRingBufferClass *ring_parent_class = NULL;
|
||||||
|
|
||||||
/* ringbuffer abstract base class */
|
static OSStatus gst_osx_ring_buffer_render_notify (GstOsxRingBuffer * osxbuf,
|
||||||
|
AudioUnitRenderActionFlags * ioActionFlags,
|
||||||
|
const AudioTimeStamp * inTimeStamp, unsigned int inBusNumber,
|
||||||
|
unsigned int inNumberFrames, AudioBufferList * ioData);
|
||||||
|
|
||||||
GType
|
static AudioBufferList *buffer_list_alloc (int channels, int size);
|
||||||
gst_osx_ring_buffer_get_type (void)
|
static void buffer_list_free (AudioBufferList * list);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_osx_ring_buffer_do_init (GType type)
|
||||||
{
|
{
|
||||||
static GType ringbuffer_type = 0;
|
GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
|
||||||
|
"OSX Audio Elements");
|
||||||
|
}
|
||||||
|
|
||||||
if (!ringbuffer_type) {
|
GST_BOILERPLATE_FULL (GstOsxRingBuffer, gst_osx_ring_buffer, GstRingBuffer,
|
||||||
static const GTypeInfo ringbuffer_info = {
|
GST_TYPE_RING_BUFFER, gst_osx_ring_buffer_do_init);
|
||||||
sizeof (GstOsxRingBufferClass),
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
(GClassInitFunc) gst_osx_ring_buffer_class_init,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
sizeof (GstOsxRingBuffer),
|
|
||||||
0,
|
|
||||||
(GInstanceInitFunc) gst_osx_ring_buffer_init,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
|
|
||||||
"OSX Audio Elements");
|
|
||||||
GST_DEBUG ("Creating osx ring buffer type");
|
|
||||||
|
|
||||||
ringbuffer_type =
|
static void
|
||||||
g_type_register_static (GST_TYPE_RING_BUFFER, "GstOsxRingBuffer",
|
gst_osx_ring_buffer_base_init (gpointer g_class)
|
||||||
&ringbuffer_info, 0);
|
{
|
||||||
}
|
/* Nothing to do right now */
|
||||||
return ringbuffer_type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -152,159 +144,463 @@ gst_osx_ring_buffer_finalize (GObject * object)
|
||||||
G_OBJECT_CLASS (ring_parent_class)->finalize (object);
|
G_OBJECT_CLASS (ring_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AudioUnit
|
||||||
|
gst_osx_ring_buffer_create_audio_unit (GstOsxRingBuffer * osxbuf,
|
||||||
|
gboolean input, AudioDeviceID device_id)
|
||||||
|
{
|
||||||
|
ComponentDescription desc;
|
||||||
|
Component comp;
|
||||||
|
OSStatus status;
|
||||||
|
AudioUnit unit;
|
||||||
|
UInt32 enableIO;
|
||||||
|
|
||||||
|
/* Create a HALOutput AudioUnit.
|
||||||
|
* This is the lowest-level output API that is actually sensibly usable
|
||||||
|
* (the lower level ones require that you do channel-remapping yourself,
|
||||||
|
* and the CoreAudio channel mapping is sufficiently complex that doing
|
||||||
|
* so would be very difficult)
|
||||||
|
*
|
||||||
|
* Note that for input we request an output unit even though we will do
|
||||||
|
* input with it: http://developer.apple.com/technotes/tn2002/tn2091.html
|
||||||
|
*/
|
||||||
|
desc.componentType = kAudioUnitType_Output;
|
||||||
|
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||||
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
|
desc.componentFlags = 0;
|
||||||
|
desc.componentFlagsMask = 0;
|
||||||
|
|
||||||
|
comp = FindNextComponent (NULL, &desc);
|
||||||
|
if (comp == NULL) {
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Couldn't find HALOutput component");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = OpenAComponent (comp, &unit);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Couldn't open HALOutput component");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input) {
|
||||||
|
/* enable input */
|
||||||
|
enableIO = 1;
|
||||||
|
status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, /* 1 = input element */
|
||||||
|
&enableIO, sizeof (enableIO));
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
CloseComponent (unit);
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Failed to enable input: %lx", status);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* disable output */
|
||||||
|
enableIO = 0;
|
||||||
|
status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, /* 0 = output element */
|
||||||
|
&enableIO, sizeof (enableIO));
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
CloseComponent (unit);
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Failed to disable output: %lx", status);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify which device we're using. */
|
||||||
|
GST_DEBUG_OBJECT (osxbuf, "Setting device to %d", (int) device_id);
|
||||||
|
status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, /* N/A for global */
|
||||||
|
&device_id, sizeof (AudioDeviceID));
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
CloseComponent (unit);
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Failed to set device: %lx", status);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (osxbuf, "Create HALOutput AudioUnit: %p", unit);
|
||||||
|
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_osx_ring_buffer_open_device (GstRingBuffer * buf)
|
gst_osx_ring_buffer_open_device (GstRingBuffer * buf)
|
||||||
{
|
{
|
||||||
/* stub, we need to open device..maybe do nothing */
|
GstOsxRingBuffer *osxbuf;
|
||||||
|
GstOsxAudioSink *sink;
|
||||||
|
GstOsxAudioSrc *src;
|
||||||
|
AudioStreamBasicDescription asbd_in;
|
||||||
|
OSStatus status;
|
||||||
|
UInt32 propertySize;
|
||||||
|
|
||||||
|
osxbuf = GST_OSX_RING_BUFFER (buf);
|
||||||
|
sink = NULL;
|
||||||
|
src = NULL;
|
||||||
|
|
||||||
|
osxbuf->audiounit = gst_osx_ring_buffer_create_audio_unit (osxbuf,
|
||||||
|
osxbuf->is_src, osxbuf->device_id);
|
||||||
|
|
||||||
|
if (osxbuf->is_src) {
|
||||||
|
src = GST_OSX_AUDIO_SRC (GST_OBJECT_PARENT (buf));
|
||||||
|
|
||||||
|
propertySize = sizeof (asbd_in);
|
||||||
|
status = AudioUnitGetProperty (osxbuf->audiounit,
|
||||||
|
kAudioUnitProperty_StreamFormat,
|
||||||
|
kAudioUnitScope_Input, 1, &asbd_in, &propertySize);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
CloseComponent (osxbuf->audiounit);
|
||||||
|
osxbuf->audiounit = NULL;
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Unable to obtain device properties: %lx",
|
||||||
|
status);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
src->deviceChannels = asbd_in.mChannelsPerFrame;
|
||||||
|
} else {
|
||||||
|
sink = GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf));
|
||||||
|
|
||||||
|
/* needed for the sink's volume control */
|
||||||
|
sink->audiounit = osxbuf->audiounit;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_osx_ring_buffer_close_device (GstRingBuffer * buf)
|
gst_osx_ring_buffer_close_device (GstRingBuffer * buf)
|
||||||
{
|
{
|
||||||
/* stub, we need to close device..maybe do nothing */
|
GstOsxRingBuffer *osxbuf;
|
||||||
|
osxbuf = GST_OSX_RING_BUFFER (buf);
|
||||||
|
|
||||||
|
CloseComponent (osxbuf->audiounit);
|
||||||
|
osxbuf->audiounit = NULL;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AudioChannelLabel
|
||||||
|
gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
|
||||||
|
position, int channel)
|
||||||
|
{
|
||||||
|
switch (position) {
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_NONE:
|
||||||
|
return kAudioChannelLabel_Discrete_0 | channel;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO:
|
||||||
|
return kAudioChannelLabel_Mono;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
|
||||||
|
return kAudioChannelLabel_Left;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
|
||||||
|
return kAudioChannelLabel_Right;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
|
||||||
|
return kAudioChannelLabel_CenterSurround;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
|
||||||
|
return kAudioChannelLabel_LeftSurround;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
|
||||||
|
return kAudioChannelLabel_RightSurround;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_LFE:
|
||||||
|
return kAudioChannelLabel_LFEScreen;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
|
||||||
|
return kAudioChannelLabel_Center;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
|
||||||
|
return kAudioChannelLabel_Center; // ???
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
|
||||||
|
return kAudioChannelLabel_Center; // ???
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
|
||||||
|
return kAudioChannelLabel_LeftSurroundDirect;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
|
||||||
|
return kAudioChannelLabel_RightSurroundDirect;
|
||||||
|
default:
|
||||||
|
return kAudioChannelLabel_Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
||||||
{
|
{
|
||||||
/* Configure the output stream and allocate ringbuffer memory */
|
/* Configure the output stream and allocate ringbuffer memory */
|
||||||
GstOsxRingBuffer *osxbuf;
|
GstOsxRingBuffer *osxbuf;
|
||||||
AudioStreamBasicDescription asbd;
|
AudioStreamBasicDescription format;
|
||||||
|
AudioChannelLayout *layout = NULL;
|
||||||
OSStatus status;
|
OSStatus status;
|
||||||
UInt32 buffer_len;
|
|
||||||
UInt32 propertySize;
|
UInt32 propertySize;
|
||||||
|
int layoutSize;
|
||||||
|
int element;
|
||||||
|
int i;
|
||||||
|
AudioUnitScope scope;
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
GstStructure *structure;
|
||||||
|
GstAudioChannelPosition *positions;
|
||||||
|
UInt32 frameSize;
|
||||||
|
|
||||||
osxbuf = GST_OSX_RING_BUFFER (buf);
|
osxbuf = GST_OSX_RING_BUFFER (buf);
|
||||||
|
|
||||||
/* Fill out the audio description we're going to be using */
|
/* Fill out the audio description we're going to be using */
|
||||||
asbd.mFormatID = kAudioFormatLinearPCM;
|
format.mFormatID = kAudioFormatLinearPCM;
|
||||||
asbd.mSampleRate = (double) spec->rate;
|
format.mSampleRate = (double) spec->rate;
|
||||||
asbd.mChannelsPerFrame = spec->channels;
|
format.mChannelsPerFrame = spec->channels;
|
||||||
asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
||||||
asbd.mBytesPerFrame = spec->channels * sizeof (float);
|
format.mBytesPerFrame = spec->channels * sizeof (float);
|
||||||
asbd.mBitsPerChannel = sizeof (float) * 8;
|
format.mBitsPerChannel = sizeof (float) * 8;
|
||||||
asbd.mBytesPerPacket = spec->channels * sizeof (float);
|
format.mBytesPerPacket = spec->channels * sizeof (float);
|
||||||
asbd.mFramesPerPacket = 1;
|
format.mFramesPerPacket = 1;
|
||||||
asbd.mReserved = 0;
|
format.mReserved = 0;
|
||||||
|
|
||||||
|
/* Describe channels */
|
||||||
|
layoutSize = sizeof (AudioChannelLayout) +
|
||||||
|
spec->channels * sizeof (AudioChannelDescription);
|
||||||
|
layout = g_malloc (layoutSize);
|
||||||
|
|
||||||
|
structure = gst_caps_get_structure (spec->caps, 0);
|
||||||
|
positions = gst_audio_get_channel_positions (structure);
|
||||||
|
|
||||||
|
layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
|
||||||
|
layout->mChannelBitmap = 0; /* Not used */
|
||||||
|
layout->mNumberChannelDescriptions = spec->channels;
|
||||||
|
for (i = 0; i < spec->channels; i++) {
|
||||||
|
if (positions) {
|
||||||
|
layout->mChannelDescriptions[i].mChannelLabel =
|
||||||
|
gst_audio_channel_position_to_coreaudio_channel_label (positions[i],
|
||||||
|
i);
|
||||||
|
} else {
|
||||||
|
/* Discrete channel numbers are ORed into this */
|
||||||
|
layout->mChannelDescriptions[i].mChannelLabel =
|
||||||
|
kAudioChannelLabel_Discrete_0 | i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Others unused */
|
||||||
|
layout->mChannelDescriptions[i].mChannelFlags = 0;
|
||||||
|
layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
|
||||||
|
layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
|
||||||
|
layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (positions) {
|
||||||
|
g_free (positions);
|
||||||
|
positions = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
GST_LOG_OBJECT (osxbuf, "Format: %x, %f, %u, %x, %d, %d, %d, %d, %d",
|
GST_LOG_OBJECT (osxbuf, "Format: %x, %f, %u, %x, %d, %d, %d, %d, %d",
|
||||||
(unsigned int) asbd.mFormatID,
|
(unsigned int) format.mFormatID,
|
||||||
asbd.mSampleRate,
|
format.mSampleRate,
|
||||||
(unsigned int) asbd.mChannelsPerFrame,
|
(unsigned int) format.mChannelsPerFrame,
|
||||||
(unsigned int) asbd.mFormatFlags,
|
(unsigned int) format.mFormatFlags,
|
||||||
(unsigned int) asbd.mBytesPerFrame,
|
(unsigned int) format.mBytesPerFrame,
|
||||||
(unsigned int) asbd.mBitsPerChannel,
|
(unsigned int) format.mBitsPerChannel,
|
||||||
(unsigned int) asbd.mBytesPerPacket,
|
(unsigned int) format.mBytesPerPacket,
|
||||||
(unsigned int) asbd.mFramesPerPacket, (unsigned int) asbd.mReserved);
|
(unsigned int) format.mFramesPerPacket, (unsigned int) format.mReserved);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxbuf, "Using stream_id %d, setting output format",
|
GST_DEBUG_OBJECT (osxbuf, "Setting format for AudioUnit");
|
||||||
(int) osxbuf->stream_id);
|
|
||||||
|
|
||||||
propertySize = sizeof (asbd);
|
scope = osxbuf->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
|
||||||
status = AudioStreamSetProperty (osxbuf->stream_id, NULL, /* Change immediately */
|
element = osxbuf->is_src ? 1 : 0;
|
||||||
0, /* Master channel */
|
|
||||||
kAudioStreamPropertyVirtualFormat, propertySize, &asbd);
|
propertySize = sizeof (format);
|
||||||
|
status = AudioUnitSetProperty (osxbuf->audiounit,
|
||||||
|
kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize);
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
GST_WARNING_OBJECT (osxbuf, "Failed to set output description: %lx",
|
GST_WARNING_OBJECT (osxbuf, "Failed to set audio description: %lx", status);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = AudioUnitSetProperty (osxbuf->audiounit,
|
||||||
|
kAudioUnitProperty_AudioChannelLayout,
|
||||||
|
scope, element, layout, layoutSize);
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Failed to set output channel layout: %lx",
|
||||||
status);
|
status);
|
||||||
return FALSE;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get requested buffer length to use */
|
spec->segsize = 4096;
|
||||||
propertySize = sizeof (buffer_len);
|
|
||||||
status = AudioDeviceGetProperty (osxbuf->device_id, 0, false, /* TODO, this should be true for the source element */
|
|
||||||
kAudioDevicePropertyBufferSize, &propertySize, &buffer_len);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (osxbuf,
|
|
||||||
"AudioDeviceGetProperty returned %d when getting "
|
|
||||||
"kAudioDevicePropertyBufferSize", (int) status);
|
|
||||||
}
|
|
||||||
GST_DEBUG_OBJECT (osxbuf, "%5d osxbuf->buffer_len", (int) buffer_len);
|
|
||||||
spec->segsize = buffer_len;
|
|
||||||
spec->segtotal = 16;
|
spec->segtotal = 16;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxbuf, "osx ring buffer acquired");
|
/* create AudioBufferList needed for recording */
|
||||||
|
if (osxbuf->is_src) {
|
||||||
|
propertySize = sizeof (frameSize);
|
||||||
|
status = AudioUnitGetProperty (osxbuf->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, /* N/A for global */
|
||||||
|
&frameSize, &propertySize);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Failed to get frame size: %lx", status);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
osxbuf->recBufferList = buffer_list_alloc (format.mChannelsPerFrame,
|
||||||
|
frameSize * format.mBytesPerFrame);
|
||||||
|
}
|
||||||
|
|
||||||
buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
|
buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
|
||||||
memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));
|
memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));
|
||||||
|
|
||||||
return TRUE;
|
status = AudioUnitInitialize (osxbuf->audiounit);
|
||||||
|
if (status) {
|
||||||
|
gst_buffer_unref (buf->data);
|
||||||
|
buf->data = NULL;
|
||||||
|
|
||||||
|
if (osxbuf->recBufferList) {
|
||||||
|
buffer_list_free (osxbuf->recBufferList);
|
||||||
|
osxbuf->recBufferList = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_WARNING_OBJECT (osxbuf,
|
||||||
|
"Failed to initialise AudioUnit: %d", (int) status);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (osxbuf, "osx ring buffer acquired");
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
|
||||||
|
done:
|
||||||
|
g_free (layout);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_osx_ring_buffer_release (GstRingBuffer * buf)
|
gst_osx_ring_buffer_release (GstRingBuffer * buf)
|
||||||
{
|
{
|
||||||
/* stub, we need to deallocate ringbuffer memory */
|
|
||||||
GstOsxRingBuffer *osxbuf;
|
GstOsxRingBuffer *osxbuf;
|
||||||
|
|
||||||
osxbuf = GST_OSX_RING_BUFFER (buf);
|
osxbuf = GST_OSX_RING_BUFFER (buf);
|
||||||
|
|
||||||
|
AudioUnitUninitialize (osxbuf->audiounit);
|
||||||
|
|
||||||
gst_buffer_unref (buf->data);
|
gst_buffer_unref (buf->data);
|
||||||
buf->data = NULL;
|
buf->data = NULL;
|
||||||
|
|
||||||
|
if (osxbuf->recBufferList) {
|
||||||
|
buffer_list_free (osxbuf->recBufferList);
|
||||||
|
osxbuf->recBufferList = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_osx_ring_buffer_remove_render_callback (GstOsxRingBuffer * osxbuf)
|
||||||
|
{
|
||||||
|
AURenderCallbackStruct input;
|
||||||
|
OSStatus status;
|
||||||
|
|
||||||
|
/* Deactivate the render callback by calling SetRenderCallback with a NULL
|
||||||
|
* inputProc.
|
||||||
|
*/
|
||||||
|
input.inputProc = NULL;
|
||||||
|
input.inputProcRefCon = NULL;
|
||||||
|
|
||||||
|
status = AudioUnitSetProperty (osxbuf->audiounit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, /* N/A for global */
|
||||||
|
&input, sizeof (input));
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Failed to remove render callback");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove the RenderNotify too */
|
||||||
|
status = AudioUnitRemoveRenderNotify (osxbuf->audiounit,
|
||||||
|
(AURenderCallback) gst_osx_ring_buffer_render_notify, osxbuf);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (osxbuf, "Failed to remove render notify callback");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're deactivated.. */
|
||||||
|
osxbuf->io_proc_needs_deactivation = FALSE;
|
||||||
|
osxbuf->io_proc_active = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OSStatus
|
||||||
|
gst_osx_ring_buffer_render_notify (GstOsxRingBuffer * osxbuf,
|
||||||
|
AudioUnitRenderActionFlags * ioActionFlags,
|
||||||
|
const AudioTimeStamp * inTimeStamp,
|
||||||
|
unsigned int inBusNumber,
|
||||||
|
unsigned int inNumberFrames, AudioBufferList * ioData)
|
||||||
|
{
|
||||||
|
/* Before rendering a frame, we get the PreRender notification.
|
||||||
|
* Here, we detach the RenderCallback if we've been paused.
|
||||||
|
*
|
||||||
|
* This is necessary (rather than just directly detaching it) to work
|
||||||
|
* around some thread-safety issues in CoreAudio
|
||||||
|
*/
|
||||||
|
if ((*ioActionFlags) & kAudioUnitRenderAction_PreRender) {
|
||||||
|
if (osxbuf->io_proc_needs_deactivation) {
|
||||||
|
gst_osx_ring_buffer_remove_render_callback (osxbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_osx_ring_buffer_start (GstRingBuffer * buf)
|
gst_osx_ring_buffer_start (GstRingBuffer * buf)
|
||||||
{
|
{
|
||||||
/* stub */
|
|
||||||
OSStatus status;
|
OSStatus status;
|
||||||
GstOsxRingBuffer *osxbuf;
|
GstOsxRingBuffer *osxbuf;
|
||||||
|
AURenderCallbackStruct input;
|
||||||
|
AudioUnitPropertyID callback_type;
|
||||||
|
|
||||||
osxbuf = GST_OSX_RING_BUFFER (buf);
|
osxbuf = GST_OSX_RING_BUFFER (buf);
|
||||||
|
|
||||||
GST_DEBUG ("osx ring buffer start ioproc: 0x%p device_id %lu",
|
GST_DEBUG ("osx ring buffer start ioproc: 0x%p device_id %lu",
|
||||||
osxbuf->element->io_proc, osxbuf->device_id);
|
osxbuf->element->io_proc, osxbuf->device_id);
|
||||||
if (!osxbuf->io_proc_active) {
|
if (!osxbuf->io_proc_active) {
|
||||||
status =
|
callback_type = osxbuf->is_src ?
|
||||||
AudioDeviceAddIOProc (osxbuf->device_id, osxbuf->element->io_proc,
|
kAudioOutputUnitProperty_SetInputCallback :
|
||||||
osxbuf);
|
kAudioUnitProperty_SetRenderCallback;
|
||||||
|
|
||||||
|
input.inputProc = (AURenderCallback) osxbuf->element->io_proc;
|
||||||
|
input.inputProcRefCon = osxbuf;
|
||||||
|
|
||||||
|
status = AudioUnitSetProperty (osxbuf->audiounit, callback_type, kAudioUnitScope_Global, 0, /* N/A for global */
|
||||||
|
&input, sizeof (input));
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
GST_WARNING ("AudioDeviceAddIOProc returned %" GST_FOURCC_FORMAT,
|
GST_WARNING ("AudioUnitSetProperty returned %d", (int) status);
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
osxbuf->io_proc_active = TRUE;
|
// ### does it make sense to do this notify stuff for input mode?
|
||||||
}
|
status = AudioUnitAddRenderNotify (osxbuf->audiounit,
|
||||||
|
(AURenderCallback) gst_osx_ring_buffer_render_notify, osxbuf);
|
||||||
|
|
||||||
status = AudioDeviceStart (osxbuf->device_id, osxbuf->element->io_proc);
|
if (status) {
|
||||||
|
GST_WARNING ("AudioUnitAddRenderNotify returned %d", (int) status);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
osxbuf->io_proc_active = TRUE;
|
||||||
|
} else
|
||||||
|
osxbuf->io_proc_needs_deactivation = FALSE;
|
||||||
|
|
||||||
|
status = AudioOutputUnitStart (osxbuf->audiounit);
|
||||||
if (status) {
|
if (status) {
|
||||||
GST_WARNING ("AudioDeviceStart returned %d", (int) status);
|
GST_WARNING ("AudioOutputUnitStart returned %d", (int) status);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ###
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_osx_ring_buffer_pause (GstRingBuffer * buf)
|
gst_osx_ring_buffer_pause (GstRingBuffer * buf)
|
||||||
{
|
{
|
||||||
/* stub */
|
|
||||||
/* stop callback */
|
|
||||||
OSErr status;
|
|
||||||
GstOsxRingBuffer *osxbuf = GST_OSX_RING_BUFFER (buf);
|
GstOsxRingBuffer *osxbuf = GST_OSX_RING_BUFFER (buf);
|
||||||
|
|
||||||
GST_DEBUG ("osx ring buffer pause ioproc: 0x%p device_id %lu",
|
GST_DEBUG ("osx ring buffer pause ioproc: 0x%p device_id %lu",
|
||||||
osxbuf->element->io_proc, osxbuf->device_id);
|
osxbuf->element->io_proc, osxbuf->device_id);
|
||||||
if (osxbuf->io_proc_active) {
|
if (osxbuf->io_proc_active) {
|
||||||
status =
|
/* CoreAudio isn't threadsafe enough to do this here; we must deactivate
|
||||||
AudioDeviceRemoveIOProc (osxbuf->device_id, osxbuf->element->io_proc);
|
* the render callback elsewhere. See:
|
||||||
if (status)
|
* http://lists.apple.com/archives/Coreaudio-api/2006/Mar/msg00010.html
|
||||||
GST_WARNING ("AudioDeviceRemoveIOProc " "returned %d", (int) status);
|
*/
|
||||||
osxbuf->io_proc_active = FALSE;
|
osxbuf->io_proc_needs_deactivation = TRUE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ###
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_osx_ring_buffer_stop (GstRingBuffer * buf)
|
gst_osx_ring_buffer_stop (GstRingBuffer * buf)
|
||||||
{
|
{
|
||||||
/* stub */
|
|
||||||
OSErr status;
|
OSErr status;
|
||||||
GstOsxRingBuffer *osxbuf;
|
GstOsxRingBuffer *osxbuf;
|
||||||
|
|
||||||
|
@ -312,17 +608,14 @@ gst_osx_ring_buffer_stop (GstRingBuffer * buf)
|
||||||
|
|
||||||
GST_DEBUG ("osx ring buffer stop ioproc: 0x%p device_id %lu",
|
GST_DEBUG ("osx ring buffer stop ioproc: 0x%p device_id %lu",
|
||||||
osxbuf->element->io_proc, osxbuf->device_id);
|
osxbuf->element->io_proc, osxbuf->device_id);
|
||||||
/* stop callback */
|
|
||||||
status = AudioDeviceStop (osxbuf->device_id, osxbuf->element->io_proc);
|
|
||||||
if (status)
|
|
||||||
GST_WARNING ("AudioDeviceStop returned %d", (int) status);
|
|
||||||
|
|
||||||
|
status = AudioOutputUnitStop (osxbuf->audiounit);
|
||||||
|
if (status)
|
||||||
|
GST_WARNING ("AudioOutputUnitStop returned %d", (int) status);
|
||||||
|
|
||||||
|
// ###: why is it okay to directly remove from here but not from pause() ?
|
||||||
if (osxbuf->io_proc_active) {
|
if (osxbuf->io_proc_active) {
|
||||||
status =
|
gst_osx_ring_buffer_remove_render_callback (osxbuf);
|
||||||
AudioDeviceRemoveIOProc (osxbuf->device_id, osxbuf->element->io_proc);
|
|
||||||
if (status)
|
|
||||||
GST_WARNING ("AudioDeviceRemoveIOProc " "returned %d", (int) status);
|
|
||||||
osxbuf->io_proc_active = FALSE;
|
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -330,6 +623,57 @@ gst_osx_ring_buffer_stop (GstRingBuffer * buf)
|
||||||
static guint
|
static guint
|
||||||
gst_osx_ring_buffer_delay (GstRingBuffer * buf)
|
gst_osx_ring_buffer_delay (GstRingBuffer * buf)
|
||||||
{
|
{
|
||||||
/* stub */
|
double latency;
|
||||||
return 0;
|
UInt32 size = sizeof (double);
|
||||||
|
GstOsxRingBuffer *osxbuf;
|
||||||
|
OSStatus status;
|
||||||
|
guint samples;
|
||||||
|
|
||||||
|
osxbuf = GST_OSX_RING_BUFFER (buf);
|
||||||
|
|
||||||
|
status = AudioUnitGetProperty (osxbuf->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, /* N/A for global */
|
||||||
|
&latency, &size);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (buf, "Failed to get latency: %d", (int) status);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = latency * GST_RING_BUFFER (buf)->spec.rate;
|
||||||
|
GST_DEBUG_OBJECT (buf, "Got latency: %f seconds -> %d samples", latency,
|
||||||
|
samples);
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AudioBufferList *
|
||||||
|
buffer_list_alloc (int channels, int size)
|
||||||
|
{
|
||||||
|
AudioBufferList *list;
|
||||||
|
int total_size;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
total_size = sizeof (AudioBufferList) + 1 * sizeof (AudioBuffer);
|
||||||
|
list = (AudioBufferList *) g_malloc (total_size);
|
||||||
|
|
||||||
|
list->mNumberBuffers = 1;
|
||||||
|
for (n = 0; n < (int) list->mNumberBuffers; ++n) {
|
||||||
|
list->mBuffers[n].mNumberChannels = channels;
|
||||||
|
list->mBuffers[n].mDataByteSize = size;
|
||||||
|
list->mBuffers[n].mData = g_malloc (size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
buffer_list_free (AudioBufferList * list)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for (n = 0; n < (int) list->mNumberBuffers; ++n) {
|
||||||
|
if (list->mBuffers[n].mData)
|
||||||
|
g_free (list->mBuffers[n].mData);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (list);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
* Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* to deal in the Software without restriction, including without limitation
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
@ -47,37 +47,48 @@
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/audio/gstringbuffer.h>
|
#include <gst/audio/gstringbuffer.h>
|
||||||
#include <CoreAudio/CoreAudio.h>
|
#include <CoreAudio/CoreAudio.h>
|
||||||
|
|
||||||
#include "gstosxaudioelement.h"
|
#include "gstosxaudioelement.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define GST_TYPE_OSX_RING_BUFFER (gst_osx_ring_buffer_get_type())
|
#define GST_TYPE_OSX_RING_BUFFER \
|
||||||
#define GST_OSX_RING_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBuffer))
|
(gst_osx_ring_buffer_get_type())
|
||||||
#define GST_OSX_RING_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass))
|
#define GST_OSX_RING_BUFFER(obj) \
|
||||||
#define GST_OSX_RING_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass))
|
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBuffer))
|
||||||
#define GST_IS_OSX_RING_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSX_RING_BUFFER))
|
#define GST_OSX_RING_BUFFER_CLASS(klass) \
|
||||||
#define GST_IS_OSX_RING_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSX_RING_BUFFER))
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass))
|
||||||
|
#define GST_OSX_RING_BUFFER_GET_CLASS(obj) \
|
||||||
|
(G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass))
|
||||||
|
#define GST_IS_OSX_RING_BUFFER(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSX_RING_BUFFER))
|
||||||
|
#define GST_IS_OSX_RING_BUFFER_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSX_RING_BUFFER))
|
||||||
|
|
||||||
typedef struct _GstOsxRingBuffer GstOsxRingBuffer;
|
typedef struct _GstOsxRingBuffer GstOsxRingBuffer;
|
||||||
typedef struct _GstOsxRingBufferClass GstOsxRingBufferClass;
|
typedef struct _GstOsxRingBufferClass GstOsxRingBufferClass;
|
||||||
|
|
||||||
|
struct _GstOsxRingBuffer
|
||||||
|
{
|
||||||
|
GstRingBuffer object;
|
||||||
|
|
||||||
struct _GstOsxRingBuffer {
|
gboolean is_src;
|
||||||
GstRingBuffer object;
|
AudioUnit audiounit;
|
||||||
|
AudioDeviceID device_id;
|
||||||
AudioDeviceID device_id;
|
gboolean io_proc_active;
|
||||||
AudioStreamID stream_id;
|
gboolean io_proc_needs_deactivation;
|
||||||
gboolean io_proc_active;
|
guint buffer_len;
|
||||||
guint buffer_len;
|
guint segoffset;
|
||||||
GstOsxAudioElementInterface* element;
|
AudioBufferList * recBufferList;
|
||||||
|
GstOsxAudioElementInterface * element;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstOsxRingBufferClass {
|
struct _GstOsxRingBufferClass
|
||||||
GstRingBufferClass parent_class;
|
{
|
||||||
|
GstRingBufferClass parent_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType gst_osx_ring_buffer_get_type (void);
|
GType gst_osx_ring_buffer_get_type (void);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif /* __GST_OSX_RING_BUFFER_H__ */
|
||||||
|
|
Loading…
Reference in a new issue