summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibault Saunier <thibault.saunier@collabora.co.uk>2011-04-12 13:05:31 -0400
committerReynaldo H. Verdejo Pinochet <reynaldo.verdejo@collabora.co.uk>2011-04-22 12:27:52 -0400
commit878fe2d7bc74820e6de34cb28cbc2cfb638ec478 (patch)
treeafdfa9092c168cfa6f238ec37edf5da282d2d7d8
parentbead0f31f9973487a351d9f1143e7b53210d0c89 (diff)
Add audioflingersink and surfaceflingersink
-rw-r--r--Android.mk2
-rw-r--r--sink/audioflingersink/Android.mk63
-rw-r--r--sink/audioflingersink/GstAndroid.cpp37
-rw-r--r--sink/audioflingersink/audioflinger_wrapper.cpp470
-rw-r--r--sink/audioflingersink/audioflinger_wrapper.h86
-rw-r--r--sink/audioflingersink/gstaudioflingerringbuffer.h90
-rwxr-xr-xsink/audioflingersink/gstaudioflingersink.c1585
-rw-r--r--sink/audioflingersink/gstaudioflingersink.h70
-rw-r--r--sink/surfaceflingersink/Android.mk40
-rw-r--r--sink/surfaceflingersink/gstsurfaceflingersink.c386
-rw-r--r--sink/surfaceflingersink/gstsurfaceflingersink.h60
-rw-r--r--sink/surfaceflingersink/surfaceflinger_wrap.cpp330
-rw-r--r--sink/surfaceflingersink/surfaceflinger_wrap.h61
-rw-r--r--sink/surfaceflingersink/wrap_test.cpp71
14 files changed, 3351 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
index df3b692..fce676d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -7,3 +7,5 @@ GST_ANDROID_TOP := $(LOCAL_PATH)
include $(CLEAR_VARS)
include $(GST_ANDROID_TOP)/gstplayer/Android.mk
+include $(GST_ANDROID_TOP)/sink/audioflingersink/Android.mk
+include $(GST_ANDROID_TOP)/sink/surfaceflingersink/Android.mk
diff --git a/sink/audioflingersink/Android.mk b/sink/audioflingersink/Android.mk
new file mode 100644
index 0000000..ce11bea
--- /dev/null
+++ b/sink/audioflingersink/Android.mk
@@ -0,0 +1,63 @@
+LOCAL_PATH:= $(call my-dir)
+
+# -------------------------------------
+# gstaudioflinger library
+#
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+
+gstaudioflinger_FILES := \
+ audioflinger_wrapper.cpp \
+ gstaudioflingersink.c \
+ GstAndroid.cpp
+
+LOCAL_SRC_FILES := $(gstaudioflinger_FILES)
+LOCAL_C_INCLUDES = $(LOCAL_PATH) \
+ $(LOCAL_PATH)/include \
+ $(TOP)/frameworks/base
+
+LOCAL_CFLAGS += -DHAVE_CONFIG_H
+LOCAL_CFLAGS += -Wall -Wdeclaration-after-statement -g -O2
+LOCAL_CFLAGS += -DANDROID_USE_GSTREAMER \
+ $(shell $(PKG_CONFIG) gstreamer-plugins-bad-0.10 --cflags) \
+ $(shell $(PKG_CONFIG) gstreamer-audio-0.10 --cflags)
+
+ifeq ($(USE_AUDIO_PURE_CODEC),true)
+LOCAL_CFLAGS += -DAUDIO_PURE_CODEC
+endif
+
+LOCAL_SHARED_LIBRARIES += libdl
+LOCAL_SHARED_LIBRARIES += \
+ libgstreamer-0.10 \
+ libgstbase-0.10 \
+ libglib-2.0 \
+ libgthread-2.0 \
+ libgmodule-2.0 \
+ libgobject-2.0 \
+ libgstvideo-0.10 \
+ libgstaudio-0.10
+
+LOCAL_LDFLAGS := -L$(SYSROOT)/usr/lib -llog
+
+LOCAL_SHARED_LIBRARIES += \
+ libutils \
+ libcutils \
+ libui \
+ libhardware \
+ libandroid_runtime \
+ libmedia
+
+
+LOCAL_MODULE:= libgstaudioflinger
+LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/gstreamer-0.10
+
+#
+# define LOCAL_PRELINK_MODULE to false to not use pre-link map
+#
+LOCAL_PRELINK_MODULE := false
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+#endif # USE_HARDWARE_MM == true
diff --git a/sink/audioflingersink/GstAndroid.cpp b/sink/audioflingersink/GstAndroid.cpp
new file mode 100644
index 0000000..54d6b9b
--- /dev/null
+++ b/sink/audioflingersink/GstAndroid.cpp
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <sys/mman.h>
+
+/* Helper functions */
+#include <gst/gst.h>
+
+/* Object header */
+#include "gstaudioflingersink.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gboolean ret = TRUE;
+
+ ret &= gst_audioflinger_sink_plugin_init (plugin);
+
+ return ret;
+}
+
+/* Version number of package */
+#define VERSION "0.0.1"
+/* package name */
+#define PACKAGE "Android ST-ERICSSON"
+
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "audioflinger",
+ "Android audioflinger library for gstreamer",
+ plugin_init, VERSION, "LGPL", "libgstaudioflinger.so",
+ "http://www.stericsson.com")
diff --git a/sink/audioflingersink/audioflinger_wrapper.cpp b/sink/audioflingersink/audioflinger_wrapper.cpp
new file mode 100644
index 0000000..b9a1401
--- /dev/null
+++ b/sink/audioflingersink/audioflinger_wrapper.cpp
@@ -0,0 +1,470 @@
+/* GStreamer
+ * Copyright (C) <2009> Prajnashi S <prajnashi@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#define ENABLE_GST_PLAYER_LOG
+#include <media/AudioTrack.h>
+#include <utils/Log.h>
+#include <services/audioflinger/AudioFlinger.h>
+#include <media/MediaPlayerInterface.h>
+#include <media/libmediaplayerservice/MediaPlayerService.h>
+#include "audioflinger_wrapper.h"
+//#include <GstLog.h>
+
+
+#define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "audioflinger_wrapper"
+
+
+using namespace android;
+
+
+typedef struct _AudioFlingerDevice
+{
+ AudioTrack *audio_track;
+ bool init;
+ sp < MediaPlayerBase::AudioSink > audio_sink;
+ bool audio_sink_specified;
+} AudioFlingerDevice;
+
+
+/* commonly used macro */
+#define AUDIO_FLINGER_DEVICE(handle) ((AudioFlingerDevice*)handle)
+#define AUDIO_FLINGER_DEVICE_TRACK(handle) \
+ (AUDIO_FLINGER_DEVICE(handle)->audio_track)
+#define AUDIO_FLINGER_DEVICE_SINK(handle) \
+ (AUDIO_FLINGER_DEVICE(handle)->audio_sink)
+
+
+AudioFlingerDeviceHandle
+audioflinger_device_create ()
+{
+ AudioFlingerDevice *audiodev = NULL;
+ AudioTrack *audiotr = NULL;
+
+ // create a new instance of AudioFlinger
+ audiodev = new AudioFlingerDevice;
+ if (audiodev == NULL) {
+ LOGE ("Error to create AudioFlingerDevice\n");
+ return NULL;
+ }
+ // create AudioTrack
+ audiotr = new AudioTrack ();
+ if (audiotr == NULL) {
+ LOGE ("Error to create AudioTrack\n");
+ return NULL;
+ }
+
+ audiodev->init = false;
+ audiodev->audio_track = (AudioTrack *) audiotr;
+ audiodev->audio_sink = 0;
+ audiodev->audio_sink_specified = false;
+ LOGD ("Create AudioTrack successfully %p\n", audiodev);
+
+ return (AudioFlingerDeviceHandle) audiodev;
+}
+
+AudioFlingerDeviceHandle
+audioflinger_device_open (void *audio_sink)
+{
+ AudioFlingerDevice *audiodev = NULL;
+
+ // audio_sink shall be an MediaPlayerBase::AudioSink instance
+ if (audio_sink == NULL)
+ return NULL;
+
+ // create a new instance of AudioFlinger
+ audiodev = new AudioFlingerDevice;
+ if (audiodev == NULL) {
+ LOGE ("Error to create AudioFlingerDevice\n");
+ return NULL;
+ }
+ // set AudioSink
+
+ audiodev->audio_sink = (MediaPlayerBase::AudioSink *) audio_sink;
+ audiodev->audio_track = NULL;
+ audiodev->init = false;
+ audiodev->audio_sink_specified = true;
+ LOGD ("Open AudioSink successfully : %p\n", audiodev);
+
+ return (AudioFlingerDeviceHandle) audiodev;
+}
+
+int
+audioflinger_device_set (AudioFlingerDeviceHandle handle,
+ int streamType, int channelCount, uint32_t sampleRate, int bufferCount)
+{
+ status_t status = NO_ERROR;
+#ifndef STECONF_ANDROID_VERSION_DONUT
+ uint32_t channels = 0;
+#endif
+
+ int format = AudioSystem::PCM_16_BIT;
+
+ if (handle == NULL)
+ return -1;
+
+ if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ // bufferCount is not the number of internal buffer, but the internal
+ // buffer size
+#ifdef STECONF_ANDROID_VERSION_DONUT
+ status = AUDIO_FLINGER_DEVICE_TRACK (handle)->set (streamType, sampleRate,
+ format, channelCount);
+ LOGD ("SET : handle : %p : Set AudioTrack, status: %d, streamType: %d, sampleRate: %d, " "channelCount: %d, bufferCount: %d\n", handle, status, streamType, sampleRate, channelCount, bufferCount);
+#else
+ switch (channelCount) {
+ case 1:
+ channels = AudioSystem::CHANNEL_OUT_FRONT_LEFT;
+ break;
+ case 2:
+ channels = AudioSystem::CHANNEL_OUT_STEREO;
+ break;
+ case 0:
+ default:
+ channels = 0;
+ break;
+ }
+ status = AUDIO_FLINGER_DEVICE_TRACK (handle)->set (streamType, sampleRate,
+ format, channels /*, bufferCount */ );
+ LOGD ("SET handle : %p : Set AudioTrack, status: %d, streamType: %d, sampleRate: %d, " "channelCount: %d(%d), bufferCount: %d\n", handle, status, streamType, sampleRate, channelCount, channels, bufferCount);
+#endif
+ AUDIO_FLINGER_DEVICE_TRACK (handle)->setPositionUpdatePeriod (bufferCount);
+
+ } else if (AUDIO_FLINGER_DEVICE_SINK (handle).get ()) {
+#ifdef STECONF_ANDROID_VERSION_DONUT
+ status = AUDIO_FLINGER_DEVICE_SINK (handle)->open (sampleRate, channelCount, format /*, bufferCount */ ); //SDA
+
+ LOGD ("OPEN : handle : %p : Set AudioSink, status: %d, streamType: %d, sampleRate: %d," "channelCount: %d, bufferCount: %d\n", handle, status, streamType, sampleRate, channelCount, bufferCount);
+#else
+ channels = channelCount;
+ status = AUDIO_FLINGER_DEVICE_SINK (handle)->open (sampleRate, channels,
+ format /*, bufferCount */ );
+ LOGD ("OPEN handle : %p : Set AudioSink, status: %d, streamType: %d, sampleRate: %d," "channelCount: %d(%d), bufferCount: %d\n", handle, status, streamType, sampleRate, channelCount, channels, bufferCount);
+#endif
+ AUDIO_FLINGER_DEVICE_TRACK (handle) =
+ (AudioTrack *) (AUDIO_FLINGER_DEVICE_SINK (handle)->getTrack ());
+ if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ AUDIO_FLINGER_DEVICE_TRACK (handle)->
+ setPositionUpdatePeriod (bufferCount);
+ }
+ }
+
+ if (status != NO_ERROR)
+ return -1;
+
+ AUDIO_FLINGER_DEVICE (handle)->init = true;
+
+ return 0;
+}
+
+void
+audioflinger_device_release (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL)
+ return;
+
+ LOGD ("Enter\n");
+ if (!AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ LOGD ("handle : %p Release AudioTrack\n", handle);
+ delete AUDIO_FLINGER_DEVICE_TRACK (handle);
+ }
+ }
+ if (AUDIO_FLINGER_DEVICE_SINK (handle).get ()) {
+ LOGD ("handle : %p Release AudioSink\n", handle);
+ AUDIO_FLINGER_DEVICE_SINK (handle).clear ();
+ AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified = false;
+ }
+
+ delete AUDIO_FLINGER_DEVICE (handle);
+}
+
+
+void
+audioflinger_device_start (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return;
+
+ LOGD ("handle : %p Start Device\n", handle);
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ AUDIO_FLINGER_DEVICE_SINK (handle)->start ();
+ } else {
+ AUDIO_FLINGER_DEVICE_TRACK (handle)->start ();
+ }
+}
+
+void
+audioflinger_device_stop (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return;
+
+ LOGD ("handle : %p Stop Device\n", handle);
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ AUDIO_FLINGER_DEVICE_SINK (handle)->stop ();
+ } else {
+ AUDIO_FLINGER_DEVICE_TRACK (handle)->stop ();
+ }
+
+}
+
+void
+audioflinger_device_flush (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return;
+
+ LOGD ("handle : %p Flush device\n", handle);
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ AUDIO_FLINGER_DEVICE_SINK (handle)->flush ();
+ } else {
+ AUDIO_FLINGER_DEVICE_TRACK (handle)->flush ();
+ }
+}
+
+void
+audioflinger_device_pause (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return;
+
+ LOGD ("handle : %p Pause Device\n", handle);
+
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ AUDIO_FLINGER_DEVICE_SINK (handle)->pause ();
+ } else {
+ AUDIO_FLINGER_DEVICE_TRACK (handle)->pause ();
+ }
+
+}
+
+void
+audioflinger_device_mute (AudioFlingerDeviceHandle handle, int mute)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return;
+
+ LOGD ("handle : %p Mute Device\n", handle);
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ // do nothing here, because the volume/mute is set in media service layer
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ AUDIO_FLINGER_DEVICE_TRACK (handle)->mute ((bool) mute);
+ }
+}
+
+int
+audioflinger_device_muted (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return -1;
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ // do nothing here, because the volume/mute is set in media service layer
+ return -1;
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ return (int) AUDIO_FLINGER_DEVICE_TRACK (handle)->muted ();
+ }
+ return -1;
+}
+
+
+void
+audioflinger_device_set_volume (AudioFlingerDeviceHandle handle, float left,
+ float right)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return;
+
+ LOGD ("handle : %p Set volume Device %f,%f\n", handle, left, right);
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ // do nothing here, because the volume/mute is set in media service layer
+ return;
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ AUDIO_FLINGER_DEVICE_TRACK (handle)->setVolume (left, right);
+ }
+}
+
+ssize_t
+audioflinger_device_write (AudioFlingerDeviceHandle handle, const void *buffer,
+ size_t size)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return -1;
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ return AUDIO_FLINGER_DEVICE_SINK (handle)->write (buffer, size);
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ return AUDIO_FLINGER_DEVICE_TRACK (handle)->write (buffer, size);
+ }
+#ifndef STECONF_ANDROID_VERSION_DONUT
+ return -1;
+#endif
+}
+
+int
+audioflinger_device_frameCount (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return -1;
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ return (int) AUDIO_FLINGER_DEVICE_SINK (handle)->frameCount ();
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ return (int) AUDIO_FLINGER_DEVICE_TRACK (handle)->frameCount ();
+ }
+ return -1;
+}
+
+int
+audioflinger_device_frameSize (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return -1;
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ return (int) AUDIO_FLINGER_DEVICE_SINK (handle)->frameSize ();
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ return (int) AUDIO_FLINGER_DEVICE_TRACK (handle)->frameSize ();
+ }
+#ifndef STECONF_ANDROID_VERSION_DONUT
+ return -1;
+#endif
+}
+
+int64_t
+audioflinger_device_latency (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return -1;
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ return (int64_t) AUDIO_FLINGER_DEVICE_SINK (handle)->latency ();
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ return (int64_t) AUDIO_FLINGER_DEVICE_TRACK (handle)->latency ();
+ }
+ return -1;
+}
+
+int
+audioflinger_device_format (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return -1;
+
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ // do nothing here, MediaPlayerBase::AudioSink doesn't provide format()
+ // interface
+ return -1;
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ return (int) AUDIO_FLINGER_DEVICE_TRACK (handle)->format ();
+ }
+ return -1;
+}
+
+int
+audioflinger_device_channelCount (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return -1;
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ return (int) AUDIO_FLINGER_DEVICE_SINK (handle)->channelCount ();
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ return (int) AUDIO_FLINGER_DEVICE_TRACK (handle)->channelCount ();
+ }
+ return -1;
+}
+
+uint32_t
+audioflinger_device_sampleRate (AudioFlingerDeviceHandle handle)
+{
+ if (handle == NULL || AUDIO_FLINGER_DEVICE (handle)->init == false)
+ return 0;
+ if (AUDIO_FLINGER_DEVICE (handle)->audio_sink_specified) {
+ // do nothing here, MediaPlayerBase::AudioSink doesn't provide sampleRate()
+ // interface
+ return -1;
+ } else if (AUDIO_FLINGER_DEVICE_TRACK (handle)) {
+ return (int) AUDIO_FLINGER_DEVICE_TRACK (handle)->getSampleRate ();
+ }
+ return (-1);
+}
+
+int
+audioflinger_device_obtain_buffer (AudioFlingerDeviceHandle handle,
+ void **buffer_handle, int8_t ** data, size_t * samples, uint64_t offset)
+{
+ AudioTrack *track = AUDIO_FLINGER_DEVICE_TRACK (handle);
+ status_t res;
+ AudioTrack::Buffer * audioBuffer;
+
+ if (track == 0)
+ return (-1);
+ audioBuffer = new AudioTrack::Buffer ();
+ audioBuffer->frameCount = *samples;
+// res = track->obtainBufferAtOffset (audioBuffer, offset, -1);
+ res = track->obtainBuffer (audioBuffer, -1);
+ if (res < 0) {
+ delete audioBuffer;
+
+ return (int) res;
+ }
+
+ *samples = audioBuffer->frameCount;
+ *buffer_handle = static_cast < void *>(audioBuffer);
+ *data = audioBuffer->i8;
+
+ return res;
+}
+
+void
+audioflinger_device_release_buffer (AudioFlingerDeviceHandle handle,
+ void *buffer_handle)
+{
+ AudioTrack *track = AUDIO_FLINGER_DEVICE_TRACK (handle);
+ AudioTrack::Buffer * audioBuffer =
+ static_cast < AudioTrack::Buffer * >(buffer_handle);
+
+ if (track == 0)
+ return;
+
+ track->releaseBuffer (audioBuffer);
+ delete audioBuffer;
+}
+
+uint32_t
+audioflinger_device_get_position (AudioFlingerDeviceHandle handle)
+{
+ status_t status;
+ uint32_t ret = -1;
+ AudioTrack *track = AUDIO_FLINGER_DEVICE_TRACK (handle);
+
+ if (track == 0)
+ return (-1);
+
+ status = track->getPosition (&ret);
+
+ return ret;
+}
diff --git a/sink/audioflingersink/audioflinger_wrapper.h b/sink/audioflingersink/audioflinger_wrapper.h
new file mode 100644
index 0000000..0556dde
--- /dev/null
+++ b/sink/audioflingersink/audioflinger_wrapper.h
@@ -0,0 +1,86 @@
+/* GStreamer
+ * Copyright (C) <2009> Prajnashi S <prajnashi@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This file defines APIs to convert C++ AudioFlinder/AudioTrack
+ * interface to C interface
+ */
+#ifndef __AUDIOFLINGER_WRAPPER_H__
+#define __AUDIOFLINGER_WRAPPER_H__
+
+#define LATE 0x80000002
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ typedef void *AudioFlingerDeviceHandle;
+
+ AudioFlingerDeviceHandle audioflinger_device_create ();
+
+ AudioFlingerDeviceHandle audioflinger_device_open (void *audio_sink);
+
+ int audioflinger_device_set (AudioFlingerDeviceHandle handle,
+ int streamType, int channelCount, uint32_t sampleRate, int bufferCount);
+
+ void audioflinger_device_release (AudioFlingerDeviceHandle handle);
+
+ void audioflinger_device_start (AudioFlingerDeviceHandle handle);
+
+ void audioflinger_device_stop (AudioFlingerDeviceHandle handle);
+
+ ssize_t audioflinger_device_write (AudioFlingerDeviceHandle handle,
+ const void *buffer, size_t size);
+
+ void audioflinger_device_flush (AudioFlingerDeviceHandle handle);
+
+ void audioflinger_device_pause (AudioFlingerDeviceHandle handle);
+
+ void audioflinger_device_mute (AudioFlingerDeviceHandle handle, int mute);
+
+ int audioflinger_device_muted (AudioFlingerDeviceHandle handle);
+
+ void audioflinger_device_set_volume (AudioFlingerDeviceHandle handle,
+ float left, float right);
+
+ int audioflinger_device_frameCount (AudioFlingerDeviceHandle handle);
+
+ int audioflinger_device_frameSize (AudioFlingerDeviceHandle handle);
+
+ int64_t audioflinger_device_latency (AudioFlingerDeviceHandle handle);
+
+ int audioflinger_device_format (AudioFlingerDeviceHandle handle);
+
+ int audioflinger_device_channelCount (AudioFlingerDeviceHandle handle);
+
+ uint32_t audioflinger_device_sampleRate (AudioFlingerDeviceHandle handle);
+
+ int audioflinger_device_obtain_buffer (AudioFlingerDeviceHandle handle,
+ void **buffer_handle, int8_t ** data, size_t * samples, uint64_t offset);
+ void audioflinger_device_release_buffer (AudioFlingerDeviceHandle handle,
+ void *buffer_handle);
+
+ uint32_t audioflinger_device_get_position (AudioFlingerDeviceHandle handle);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AUDIOFLINGER_WRAPPER_H__ */
diff --git a/sink/audioflingersink/gstaudioflingerringbuffer.h b/sink/audioflingersink/gstaudioflingerringbuffer.h
new file mode 100644
index 0000000..8ccd7bb
--- /dev/null
+++ b/sink/audioflingersink/gstaudioflingerringbuffer.h
@@ -0,0 +1,90 @@
+/* GStreamer
+ * Copyright (C) 2010 Alessandro Decina <alessandro.decina@collabora.co.uk>
+ *
+ * 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_AUDIO_FLINGER_RING_BUFFER_H
+#define GST_AUDIO_FLINGER_RING_BUFFER_H
+
+#include <string.h>
+
+#include "gstaudiosink.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_audio_sink_debug);
+#define GST_CAT_DEFAULT gst_audio_sink_debug
+
+#define GST_TYPE_AUDIORING_BUFFER \
+ (gst_audioringbuffer_get_type())
+#define GST_AUDIORING_BUFFER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIORING_BUFFER,GstAudioRingBuffer))
+#define GST_AUDIORING_BUFFER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIORING_BUFFER,GstAudioRingBufferClass))
+#define GST_AUDIORING_BUFFER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_AUDIORING_BUFFER, GstAudioRingBufferClass))
+#define GST_AUDIORING_BUFFER_CAST(obj) \
+ ((GstAudioRingBuffer *)obj)
+#define GST_IS_AUDIORING_BUFFER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIORING_BUFFER))
+#define GST_IS_AUDIORING_BUFFER_CLASS(klass)\
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIORING_BUFFER))
+
+typedef struct _GstAudioRingBuffer GstAudioRingBuffer;
+typedef struct _GstAudioRingBufferClass GstAudioRingBufferClass;
+
+#define GST_AUDIORING_BUFFER_GET_COND(buf) (((GstAudioRingBuffer *)buf)->cond)
+#define GST_AUDIORING_BUFFER_WAIT(buf) (g_cond_wait (GST_AUDIORING_BUFFER_GET_COND (buf), GST_OBJECT_GET_LOCK (buf)))
+#define GST_AUDIORING_BUFFER_SIGNAL(buf) (g_cond_signal (GST_AUDIORING_BUFFER_GET_COND (buf)))
+#define GST_AUDIORING_BUFFER_BROADCAST(buf)(g_cond_broadcast (GST_AUDIORING_BUFFER_GET_COND (buf)))
+
+struct _GstAudioRingBuffer
+{
+ GstRingBuffer object;
+
+ gboolean running;
+ gint queuedseg;
+
+ GCond *cond;
+};
+
+struct _GstAudioRingBufferClass
+{
+ GstRingBufferClass parent_class;
+};
+
+static void gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass);
+static void gst_audioringbuffer_init (GstAudioRingBuffer * ringbuffer,
+ GstAudioRingBufferClass * klass);
+static void gst_audioringbuffer_dispose (GObject * object);
+static void gst_audioringbuffer_finalize (GObject * object);
+
+static GstRingBufferClass *ring_parent_class = NULL;
+
+static gboolean gst_audioringbuffer_open_device (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_close_device (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_acquire (GstRingBuffer * buf,
+ GstRingBufferSpec * spec);
+static gboolean gst_audioringbuffer_release (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_start (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_pause (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_stop (GstRingBuffer * buf);
+static guint gst_audioringbuffer_delay (GstRingBuffer * buf);
+static gboolean gst_audioringbuffer_activate (GstRingBuffer * buf,
+ gboolean active);
+
+GType gst_audioringbuffer_get_type (void);
+
+#endif /* GST_AUDIO_FLINGER_RING_BUFFER_H */
diff --git a/sink/audioflingersink/gstaudioflingersink.c b/sink/audioflingersink/gstaudioflingersink.c
new file mode 100755
index 0000000..9d13af2
--- /dev/null
+++ b/sink/audioflingersink/gstaudioflingersink.c
@@ -0,0 +1,1585 @@
+/* GStreamer
+ * Copyright (C) <2009> Prajnashi S <prajnashi@gmail.com>
+ * Copyright (C) <2011> Alessandro Decina <alessandro.decina@collabora.co.uk>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-audioflindersink
+ *
+ * This element lets you output sound using the Audio Flinger system in Android
+ *
+ * Note that you should almost always use generic audio conversion elements
+ * like audioconvert and audioresample in front of an audiosink to make sure
+ * your pipeline works under all circumstances (those conversion elements will
+ * act in passthrough-mode if no conversion is necessary).
+ */
+
+#ifdef HAVE_CONFIG_H
+//#include "config.h"
+#endif
+#include "gstaudioflingersink.h"
+#include <utils/Log.h>
+
+
+#define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "GstAudioFlingerSink"
+
+
+#define DEFAULT_BUFFERTIME (500*GST_MSECOND) / (GST_USECOND)
+#define DEFAULT_LATENCYTIME (50*GST_MSECOND) / (GST_USECOND)
+#define DEFAULT_VOLUME 1.0
+#define DEFAULT_MUTE FALSE
+#define DEFAULT_EXPORT_SYSTEM_AUDIO_CLOCK TRUE
+
+/*
+ * PROPERTY_ID
+ */
+enum
+{
+ PROP_NULL,
+ PROP_VOLUME,
+ PROP_MUTE,
+ PROP_AUDIO_SINK,
+};
+
+GST_DEBUG_CATEGORY_STATIC (audioflinger_debug);
+#define GST_CAT_DEFAULT audioflinger_debug
+
+#define GST_TYPE_ANDROID_AUDIORING_BUFFER \
+ (gst_android_audioringbuffer_get_type())
+#define GST_ANDROID_AUDIORING_BUFFER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ANDROID_AUDIORING_BUFFER,GstAndroidAudioRingBuffer))
+#define GST_ANDROID_AUDIORING_BUFFER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ANDROID_AUDIORING_BUFFER,GstAndroidAudioRingBufferClass))
+#define GST_ANDROID_AUDIORING_BUFFER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ANDROID_AUDIORING_BUFFER, GstAndroidAudioRingBufferClass))
+#define GST_ANDROID_AUDIORING_BUFFER_CAST(obj) \
+ ((GstAndroidAudioRingBuffer *)obj)
+#define GST_IS_ANDROID_AUDIORING_BUFFER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ANDROID_AUDIORING_BUFFER))
+#define GST_IS_ANDROID_AUDIORING_BUFFER_CLASS(klass)\
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ANDROID_AUDIORING_BUFFER))
+
+typedef struct _GstAndroidAudioRingBuffer GstAndroidAudioRingBuffer;
+typedef struct _GstAndroidAudioRingBufferClass GstAndroidAudioRingBufferClass;
+
+#define GST_ANDROID_AUDIORING_BUFFER_GET_COND(buf) (((GstAndroidAudioRingBuffer *)buf)->cond)
+#define GST_ANDROID_AUDIORING_BUFFER_WAIT(buf) (g_cond_wait (GST_ANDROID_ANDROID_AUDIORING_BUFFER_GET_COND (buf), GST_OBJECT_GET_LOCK (buf)))
+#define GST_ANDROID_AUDIORING_BUFFER_SIGNAL(buf) (g_cond_signal (GST_ANDROID_ANDROID_AUDIORING_BUFFER_GET_COND (buf)))
+#define GST_ANDROID_AUDIORING_BUFFER_BROADCAST(buf)(g_cond_broadcast (GST_ANDROID_ANDROID_AUDIORING_BUFFER_GET_COND (buf)))
+
+struct _GstAndroidAudioRingBuffer
+{
+ GstRingBuffer object;
+
+ gboolean running;
+ gint queuedseg;
+
+ GCond *cond;
+};
+
+struct _GstAndroidAudioRingBufferClass
+{
+ GstRingBufferClass parent_class;
+};
+
+static void gst_android_audioringbuffer_dispose (GObject * object);
+static void gst_android_audioringbuffer_finalize (GObject * object);
+
+static GstRingBufferClass *ring_parent_class = NULL;
+
+static gboolean gst_android_audioringbuffer_open_device (GstRingBuffer * buf);
+static gboolean gst_android_audioringbuffer_close_device (GstRingBuffer * buf);
+static gboolean gst_android_audioringbuffer_acquire (GstRingBuffer * buf,
+ GstRingBufferSpec * spec);
+static gboolean gst_android_audioringbuffer_release (GstRingBuffer * buf);
+static gboolean gst_android_audioringbuffer_start (GstRingBuffer * buf);
+static gboolean gst_android_audioringbuffer_pause (GstRingBuffer * buf);
+static gboolean gst_android_audioringbuffer_stop (GstRingBuffer * buf);
+static gboolean gst_android_audioringbuffer_activate (GstRingBuffer * buf,
+ gboolean active);
+static void gst_android_audioringbuffer_clear (GstRingBuffer * buf);
+static guint gst_android_audioringbuffer_commit (GstRingBuffer * buf,
+ guint64 * sample, guchar * data, gint in_samples, gint out_samples,
+ gint * accum);
+
+static void gst_audioflinger_sink_dispose (GObject * object);
+static void gst_audioflinger_sink_finalise (GObject * object);
+
+static void gst_audioflinger_sink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_audioflinger_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+
+static GstCaps *gst_audioflinger_sink_getcaps (GstBaseSink * bsink);
+
+static gboolean gst_audioflinger_sink_open (GstAudioFlingerSink * asink);
+static gboolean gst_audioflinger_sink_close (GstAudioFlingerSink * asink);
+static gboolean gst_audioflinger_sink_prepare (GstAudioFlingerSink * asink,
+ GstRingBufferSpec * spec);
+static gboolean gst_audioflinger_sink_unprepare (GstAudioFlingerSink * asink);
+static void gst_audioflinger_sink_reset (GstAudioFlingerSink * asink,
+ gboolean create_clock);
+static void gst_audioflinger_sink_set_mute (GstAudioFlingerSink *
+ audioflinger_sink, gboolean mute);
+static void gst_audioflinger_sink_set_volume (GstAudioFlingerSink *
+ audioflinger_sink, float volume);
+static gboolean gst_audioflinger_sink_event (GstBaseSink * bsink,
+ GstEvent * event);
+static GstRingBuffer *gst_audioflinger_sink_create_ringbuffer (GstBaseAudioSink
+ * sink);
+static GstClockTime gst_audioflinger_sink_get_time (GstClock * clock,
+ gpointer user_data);
+static GstFlowReturn gst_audioflinger_sink_preroll (GstBaseSink * bsink,
+ GstBuffer * buffer);
+static GstClockTime gst_audioflinger_sink_system_audio_clock_get_time (GstClock
+ * clock, gpointer user_data);
+static GstClock *gst_audioflinger_sink_provide_clock (GstElement * elem);
+static GstStateChangeReturn gst_audioflinger_sink_change_state (GstElement *
+ element, GstStateChange transition);
+
+static GstStaticPadTemplate audioflingersink_sink_factory =
+ GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw-int, "
+ "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, "
+ "signed = (boolean) { TRUE }, "
+ "width = (int) 16, "
+ "depth = (int) 16, "
+ "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; ")
+ );
+
+G_DEFINE_TYPE (GstAndroidAudioRingBuffer,
+ gst_android_audioringbuffer, GST_TYPE_RING_BUFFER);
+GST_BOILERPLATE (GstAudioFlingerSink, gst_audioflinger_sink, GstAudioSink,
+ GST_TYPE_AUDIO_SINK);
+
+static void
+gst_android_audioringbuffer_class_init (GstAndroidAudioRingBufferClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstRingBufferClass *gstringbuffer_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstringbuffer_class = GST_RING_BUFFER_CLASS (klass);
+
+ ring_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->dispose = gst_android_audioringbuffer_dispose;
+ gobject_class->finalize = gst_android_audioringbuffer_finalize;
+
+ gstringbuffer_class->open_device =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_open_device);
+ gstringbuffer_class->close_device =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_close_device);
+ gstringbuffer_class->acquire =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_acquire);
+ gstringbuffer_class->release =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_release);
+ gstringbuffer_class->start =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_start);
+ gstringbuffer_class->pause =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_pause);
+ gstringbuffer_class->resume =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_start);
+ gstringbuffer_class->stop =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_stop);
+ gstringbuffer_class->clear_all =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_clear);
+ gstringbuffer_class->commit =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_commit);
+
+#if 0
+ gstringbuffer_class->delay =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_delay);
+#endif
+ gstringbuffer_class->activate =
+ GST_DEBUG_FUNCPTR (gst_android_audioringbuffer_activate);
+}
+
+static void
+gst_android_audioringbuffer_init (G_GNUC_UNUSED GstAndroidAudioRingBuffer *
+ ringbuffer)
+{
+}
+
+static void
+gst_android_audioringbuffer_dispose (GObject * object)
+{
+ G_OBJECT_CLASS (ring_parent_class)->dispose (object);
+}
+
+static void
+gst_android_audioringbuffer_finalize (GObject * object)
+{
+ G_OBJECT_CLASS (ring_parent_class)->finalize (object);
+}
+
+static gboolean
+gst_android_audioringbuffer_open_device (GstRingBuffer * buf)
+{
+ GstAudioFlingerSink *sink;
+ gboolean result = TRUE;
+ LOGD (">gst_android_audioringbuffer_open_device");
+ sink = GST_AUDIOFLINGERSINK (GST_OBJECT_PARENT (buf));
+ result = gst_audioflinger_sink_open (sink);
+ GST_INFO_OBJECT (sink, "open device result %d", result);
+
+ if (!result)
+ goto could_not_open;
+
+ return result;
+
+could_not_open:
+ {
+ GST_DEBUG_OBJECT (sink, "could not open device");
+ LOGE ("could not open device");
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_android_audioringbuffer_close_device (GstRingBuffer * buf)
+{
+ GstAudioFlingerSink *sink;
+ gboolean result = TRUE;
+
+ LOGD (">gst_android_audioringbuffer_close_device");
+
+ sink = GST_AUDIOFLINGERSINK (GST_OBJECT_PARENT (buf));
+
+ result = gst_audioflinger_sink_close (sink);
+
+ if (!result)
+ goto could_not_close;
+
+ return result;
+
+could_not_close:
+ {
+ GST_DEBUG_OBJECT (sink, "could not close device");
+ LOGE ("could not close device");
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_android_audioringbuffer_acquire (GstRingBuffer * buf,
+ GstRingBufferSpec * spec)
+{
+ GstAudioFlingerSink *sink;
+ gboolean result = FALSE;
+
+ LOGD (">gst_android_audioringbuffer_acquire");
+
+ sink = GST_AUDIOFLINGERSINK (GST_OBJECT_PARENT (buf));
+
+ result = gst_audioflinger_sink_prepare (sink, spec);
+
+ if (!result)
+ goto could_not_prepare;
+
+ return TRUE;
+
+ /* ERRORS */
+could_not_prepare:
+ {
+ GST_DEBUG_OBJECT (sink, "could not prepare device");
+ LOGE ("could not close device");
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_android_audioringbuffer_activate (G_GNUC_UNUSED GstRingBuffer * buf,
+ G_GNUC_UNUSED gboolean active)
+{
+ return TRUE;
+}
+
+/* function is called with LOCK */
+static gboolean
+gst_android_audioringbuffer_release (GstRingBuffer * buf)
+{
+ GstAudioFlingerSink *sink;
+ gboolean result = FALSE;
+ LOGD (">gst_android_audioringbuffer_release");
+
+ sink = GST_AUDIOFLINGERSINK (GST_OBJECT_PARENT (buf));
+
+ result = gst_audioflinger_sink_unprepare (sink);
+
+ if (!result)
+ goto could_not_unprepare;
+
+ GST_DEBUG_OBJECT (sink, "unprepared");
+
+ return result;
+
+could_not_unprepare:
+ {
+ GST_DEBUG_OBJECT (sink, "could not unprepare device");
+ LOGE ("could not unprepare device");
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_android_audioringbuffer_start (GstRingBuffer * buf)
+{
+ GstAudioFlingerSink *asink;
+ GstAndroidAudioRingBuffer *abuf;
+
+ abuf = GST_ANDROID_AUDIORING_BUFFER_CAST (buf);
+ asink = GST_AUDIOFLINGERSINK (GST_OBJECT_PARENT (abuf));
+
+ GST_INFO_OBJECT (buf, "starting ringbuffer");
+ LOGD ("starting ringbuffer");
+
+ audioflinger_device_start (asink->audioflinger_device);
+
+ return TRUE;
+}
+
+static gboolean
+gst_android_audioringbuffer_pause (GstRingBuffer * buf)
+{
+ GstAudioFlingerSink *asink;
+ GstAndroidAudioRingBuffer *abuf;
+
+ abuf = GST_ANDROID_AUDIORING_BUFFER_CAST (buf);
+ asink = GST_AUDIOFLINGERSINK (GST_OBJECT_PARENT (abuf));
+
+ GST_INFO_OBJECT (buf, "pausing ringbuffer");
+ LOGD ("pausing ringbuffer");
+
+ audioflinger_device_pause (asink->audioflinger_device);
+
+ return TRUE;
+}
+
+static gboolean
+gst_android_audioringbuffer_stop (GstRingBuffer * buf)
+{
+ GstAudioFlingerSink *asink;
+ GstAndroidAudioRingBuffer *abuf;
+
+ abuf = GST_ANDROID_AUDIORING_BUFFER_CAST (buf);
+ asink = GST_AUDIOFLINGERSINK (GST_OBJECT_PARENT (abuf));
+
+ GST_INFO_OBJECT (buf, "stopping ringbuffer");
+ LOGD ("stopping ringbuffer");
+
+ audioflinger_device_stop (asink->audioflinger_device);
+
+ return TRUE;
+}
+
+#if 0
+static guint
+gst_android_audioringbuffer_delay (GstRingBuffer * buf)
+{
+ return 0;
+}
+#endif
+
+static void
+gst_android_audioringbuffer_clear (GstRingBuffer * buf)
+{
+ GstAudioFlingerSink *asink;
+ GstAndroidAudioRingBuffer *abuf;
+ guint32 position;
+
+ abuf = GST_ANDROID_AUDIORING_BUFFER_CAST (buf);
+ asink = GST_AUDIOFLINGERSINK (GST_OBJECT_PARENT (abuf));
+
+ GST_INFO_OBJECT (buf, "clearing ringbuffer, device %p",
+ asink->audioflinger_device);
+
+ if (asink->audioflinger_device == NULL)
+ return;
+
+ GST_INFO_OBJECT (buf, "before flush");
+ audioflinger_device_flush (asink->audioflinger_device);
+ GST_INFO_OBJECT (buf, "after flush");
+// position = audioflinger_device_get_position (asink->audioflinger_device);
+ GST_INFO_OBJECT (buf, "after get position");
+ if (TRUE || position == 0) {
+ GST_INFO_OBJECT (asink, "resetting clock");
+ gst_audio_clock_reset (GST_AUDIO_CLOCK (asink->audio_clock), 0);
+ } else {
+ GST_WARNING_OBJECT (asink, "flush failed, not resetting clock");
+ }
+}
+
+#define FWD_SAMPLES(s,se,d,de) \
+G_STMT_START { \
+ /* no rate conversion */ \
+ guint towrite = MIN (se + bps - s, de - d); \
+ /* simple copy */ \
+ if (!skip) \
+ memcpy (d, s, towrite); \
+ in_samples -= towrite / bps; \
+ out_samples -= towrite / bps; \
+ s += towrite; \
+ GST_LOG ("copy %u bytes", towrite); \
+} G_STMT_END
+
+/* in_samples >= out_samples, rate > 1.0 */
+#define FWD_UP_SAMPLES(s,se,d,de) \
+G_STMT_START { \
+ guint8 *sb = s, *db = d; \
+ while (s <= se && d < de) { \
+ if (!skip) \
+ memcpy (d, s, bps); \
+ s += bps; \
+ *accum += outr; \
+ if ((*accum << 1) >= inr) { \
+ *accum -= inr; \
+ d += bps; \
+ } \
+ } \
+ in_samples -= (s - sb)/bps; \
+ out_samples -= (d - db)/bps; \
+ GST_DEBUG ("fwd_up end %d/%d",*accum,*toprocess); \
+} G_STMT_END
+
+/* out_samples > in_samples, for rates smaller than 1.0 */
+#define FWD_DOWN_SAMPLES(s,se,d,de) \
+G_STMT_START { \
+ guint8 *sb = s, *db = d; \
+ while (s <= se && d < de) { \
+ if (!skip) \
+ memcpy (d, s, bps); \
+ d += bps; \
+ *accum += inr; \
+ if ((*accum << 1) >= outr) { \
+ *accum -= outr; \
+ s += bps; \
+ } \
+ } \
+ in_samples -= (s - sb)/bps; \
+ out_samples -= (d - db)/bps; \
+ GST_DEBUG ("fwd_down end %d/%d",*accum,*toprocess); \
+} G_STMT_END
+
+#define REV_UP_SAMPLES(s,se,d,de) \
+G_STMT_START { \
+ guint8 *sb = se, *db = d; \
+ while (s <= se && d < de) { \
+ if (!skip) \
+ memcpy (d, se, bps); \
+ se -= bps; \
+ *accum += outr; \
+ while (d < de && (*accum << 1) >= inr) { \
+ *accum -= inr; \
+ d += bps; \
+ } \
+ } \
+ in_samples -= (sb - se)/bps; \
+ out_samples -= (d - db)/bps; \
+ GST_DEBUG ("rev_up end %d/%d",*accum,*toprocess); \
+} G_STMT_END
+
+#define REV_DOWN_SAMPLES(s,se,d,de) \
+G_STMT_START { \
+ guint8 *sb = se, *db = d; \
+ while (s <= se && d < de) { \
+ if (!skip) \
+ memcpy (d, se, bps); \
+ d += bps; \
+ *accum += inr; \
+ while (s <= se && (*accum << 1) >= outr) { \
+ *accum -= outr; \
+ se -= bps; \
+ } \
+ } \
+ in_samples -= (sb - se)/bps; \
+ out_samples -= (d - db)/bps; \
+ GST_DEBUG ("rev_down end %d/%d",*accum,*toprocess); \
+} G_STMT_END
+
+static guint
+gst_android_audioringbuffer_commit (GstRingBuffer * buf, guint64 * sample,
+ guchar * data, gint in_samples, gint out_samples, gint * accum)
+{
+ GstBaseAudioSink *baseaudiosink;
+ GstAudioFlingerSink *asink;
+ GstAndroidAudioRingBuffer *abuf;
+ guint result;
+ guint8 *data_end;
+ gboolean reverse;
+ gint *toprocess;
+ gint inr, outr, bps;
+ guint bufsize;
+ gboolean skip = FALSE;
+ guint32 position;
+ gboolean slaved;
+ gboolean sync;
+
+ abuf = GST_ANDROID_AUDIORING_BUFFER_CAST (buf);
+ asink = GST_AUDIOFLINGERSINK (GST_OBJECT_PARENT (abuf));
+ baseaudiosink = GST_BASE_AUDIO_SINK (asink);
+ sync = gst_base_sink_get_sync (GST_BASE_SINK_CAST (asink));
+
+ GST_LOG_OBJECT (asink, "entering commit");
+
+ /* make sure the ringbuffer is started */
+ if (G_UNLIKELY (g_atomic_int_get (&buf->state) !=
+ GST_RING_BUFFER_STATE_STARTED)) {
+ /* see if we are allowed to start it */
+ if (G_UNLIKELY (g_atomic_int_get (&buf->abidata.ABI.may_start) == FALSE))
+ goto no_start;
+
+ GST_INFO_OBJECT (buf, "start!");
+ LOGD ("start!");
+ if (!gst_ring_buffer_start (buf))
+ goto start_failed;
+ }
+
+ slaved = GST_ELEMENT_CLOCK (baseaudiosink) != asink->exported_clock;
+ if (asink->last_resync_sample == -1 ||
+ (gint64) baseaudiosink->next_sample == -1) {
+ if (slaved) {
+ /* we're writing a discont buffer. Disable slaving for a while in order to
+ * fill the initial buffer needed by the audio mixer thread. This avoids
+ * some cases where audioflinger removes us from the list of active tracks
+ * because we aren't writing enough data.
+ */
+ GST_INFO_OBJECT (asink, "no previous sample, now %" G_GINT64_FORMAT
+ " disabling slaving", *sample);
+ asink->last_resync_sample = *sample;
+ g_object_set (asink, "slave-method", GST_BASE_AUDIO_SINK_SLAVE_NONE,
+ NULL);
+ asink->slaving_disabled = TRUE;
+ }
+ }
+
+ if (slaved && asink->slaving_disabled) {
+ guint64 threshold;
+
+ threshold = gst_util_uint64_scale_int (buf->spec.rate, 5, 1);
+ threshold += asink->last_resync_sample;
+
+ if (*sample >= threshold) {
+ GST_INFO_OBJECT (asink, "last sync %" G_GINT64_FORMAT
+ " reached sample %" G_GINT64_FORMAT ", enabling slaving",
+ asink->last_resync_sample, *sample);
+ g_object_set (asink, "slave-method", GST_BASE_AUDIO_SINK_SLAVE_SKEW,
+ NULL);
+ asink->slaving_disabled = FALSE;
+ }
+ }
+
+ bps = buf->spec.bytes_per_sample;
+ bufsize = buf->spec.segsize * buf->spec.segtotal;
+
+ /* our toy resampler for trick modes */
+ reverse = out_samples < 0;
+ out_samples = ABS (out_samples);
+
+ if (in_samples >= out_samples)
+ toprocess = &in_samples;
+ else
+ toprocess = &out_samples;
+
+ inr = in_samples - 1;
+ outr = out_samples - 1;
+
+ GST_LOG_OBJECT (asink, "in %d, out %d reverse %d sync %d", inr, outr,
+ reverse, sync);
+
+ /* data_end points to the last sample we have to write, not past it. This is
+ * needed to properly handle reverse playback: it points to the last sample. */
+ data_end = data + (bps * inr);
+
+ while (*toprocess > 0) {
+ if (sync) {
+ size_t avail;
+ guint towrite;
+ gint err;
+ guint8 *d, *d_end;
+ gpointer buffer_handle;
+
+ position = audioflinger_device_get_position (asink->audioflinger_device);
+ avail = out_samples;
+ buffer_handle = NULL;
+ GST_LOG_OBJECT (asink, "calling obtain buffer, position %d"
+ " offset %" G_GINT64_FORMAT " samples %" G_GSSIZE_FORMAT,
+ position, *sample, avail);
+ err = audioflinger_device_obtain_buffer (asink->audioflinger_device,
+ &buffer_handle, (int8_t **) & d, &avail, *sample);
+ GST_LOG_OBJECT (asink, "obtain buffer returned");
+// if (err != NO_ERROR) {
+ if (err != 0) {
+ GST_INFO_OBJECT (asink, "obtain buffer error %d, state %d",
+ err, buf->state);
+ LOGD ("obtain buffer error 0x%x, state %d", err, buf->state);
+
+ if (err == LATE)
+ skip = TRUE;
+ else if (buf->state != GST_RING_BUFFER_STATE_STARTED)
+ goto done;
+ else
+ goto obtain_buffer_failed;
+ }
+
+ towrite = avail * bps;
+ d_end = d + towrite;
+
+ GST_LOG_OBJECT (asink, "writing %u samples at offset %" G_GUINT64_FORMAT,
+ (guint) avail, *sample);
+
+ if (G_LIKELY (inr == outr && !reverse)) {
+ FWD_SAMPLES (data, data_end, d, d_end);
+ } else if (!reverse) {
+ if (inr >= outr) {
+ /* forward speed up */
+ FWD_UP_SAMPLES (data, data_end, d, d_end);
+ } else {
+ /* forward slow down */
+ FWD_DOWN_SAMPLES (data, data_end, d, d_end);
+ }
+ } else {
+ if (inr >= outr)
+ /* reverse speed up */
+ REV_UP_SAMPLES (data, data_end, d, d_end);
+ else
+ /* reverse slow down */
+ REV_DOWN_SAMPLES (data, data_end, d, d_end);
+ }
+
+ *sample += avail;
+
+ if (buffer_handle)
+ audioflinger_device_release_buffer (asink->audioflinger_device,
+ buffer_handle);
+ } else {
+ gint written;
+
+ written = audioflinger_device_write (asink->audioflinger_device, data,
+ *toprocess * bps);
+ if (written > 0) {
+ *toprocess -= written / bps;
+ data += written;
+ } else {
+ LOGE ("Error to write buffer(error=%d)", written);
+ GST_LOG_OBJECT (asink, "Error to write buffer(error=%d)", written);
+ goto done;
+ }
+ }
+ }
+
+ /* we consumed all samples here */
+ data = data_end + bps;
+
+done:
+ result = inr - ((data_end - data) / bps);
+ GST_LOG_OBJECT (asink, "wrote %d samples", result);
+
+ return result;
+
+ /* ERRORS */
+no_start:
+ {
+ GST_LOG_OBJECT (asink, "we can not start");
+ LOGE ("we can not start");
+ return 0;
+ }
+start_failed:
+ {
+ GST_LOG_OBJECT (asink, "failed to start the ringbuffer");
+ LOGE ("failed to start the ringbuffer");
+ return 0;
+ }
+obtain_buffer_failed:
+ {
+ GST_ELEMENT_ERROR (asink, RESOURCE, FAILED,
+ ("obtain_buffer failed"), (NULL));
+ LOGE ("obtain_buffer failed");
+ return -1;
+ }
+}
+
+static void
+gst_audioflinger_sink_dispose (GObject * object)
+{
+ GstAudioFlingerSink *audioflinger_sink = GST_AUDIOFLINGERSINK (object);
+
+ if (audioflinger_sink->probed_caps) {
+ gst_caps_unref (audioflinger_sink->probed_caps);
+ audioflinger_sink->probed_caps = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_audioflinger_sink_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details_simple (element_class,
+ "Android Audioflinger Sink",
+ "Sink/Audio",
+ "Output to android's Audioflinger service",
+ "Prajnashi S <prajnashi@gmail.com>, "
+ "Alessandro Decina <alessandro.decina@collabora.co.uk>");
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&audioflingersink_sink_factory));
+ GST_DEBUG_CATEGORY_INIT (audioflinger_debug, "audioflingersink", 0,
+ "audioflinger sink trace");
+}
+
+static void
+gst_audioflinger_sink_class_init (GstAudioFlingerSinkClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseSinkClass *gstbasesink_class;
+ GstBaseAudioSinkClass *gstbaseaudiosink_class;
+ GstAudioSinkClass *gstaudiosink_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbasesink_class = (GstBaseSinkClass *) klass;
+ gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
+ gstaudiosink_class = (GstAudioSinkClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_audioflinger_sink_dispose);
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_audioflinger_sink_finalise);
+ gobject_class->get_property =
+ GST_DEBUG_FUNCPTR (gst_audioflinger_sink_get_property);
+ gobject_class->set_property =
+ GST_DEBUG_FUNCPTR (gst_audioflinger_sink_set_property);
+
+ gstelement_class->provide_clock =
+ GST_DEBUG_FUNCPTR (gst_audioflinger_sink_provide_clock);
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_audioflinger_sink_change_state);
+
+ gstbasesink_class->get_caps =
+ GST_DEBUG_FUNCPTR (gst_audioflinger_sink_getcaps);
+
+ gstbaseaudiosink_class->create_ringbuffer =
+ GST_DEBUG_FUNCPTR (gst_audioflinger_sink_create_ringbuffer);
+
+ gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_audioflinger_sink_event);
+ gstbasesink_class->preroll =
+ GST_DEBUG_FUNCPTR (gst_audioflinger_sink_preroll);
+
+ /* Install properties */
+ g_object_class_install_property (gobject_class, PROP_MUTE,
+ g_param_spec_boolean ("mute", "Mute",
+ "Mute output", DEFAULT_MUTE, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_VOLUME,
+ g_param_spec_double ("volume", "Volume",
+ "control volume size", 0, 1.0, DEFAULT_VOLUME, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_AUDIO_SINK,
+ g_param_spec_pointer ("audiosink", "AudioSink",
+ "The pointer of MediaPlayerBase::AudioSink", G_PARAM_WRITABLE));
+}
+
+static void
+gst_audioflinger_sink_init (GstAudioFlingerSink * audioflinger_sink,
+ GstAudioFlingerSinkClass * klass)
+{
+ GST_DEBUG_OBJECT (audioflinger_sink, "initializing audioflinger_sink");
+
+ audioflinger_sink->audio_clock = NULL;
+ audioflinger_sink->system_clock = NULL;
+ audioflinger_sink->system_audio_clock = NULL;
+ audioflinger_sink->exported_clock = NULL;
+ audioflinger_sink->export_system_audio_clock =
+ DEFAULT_EXPORT_SYSTEM_AUDIO_CLOCK;
+ gst_audioflinger_sink_reset (audioflinger_sink, TRUE);
+}
+
+static void
+gst_audioflinger_sink_reset (GstAudioFlingerSink * sink, gboolean create_clocks)
+{
+
+ if (sink->audioflinger_device != NULL) {
+ audioflinger_device_release (sink->audioflinger_device);
+ sink->audioflinger_device = NULL;
+ }
+
+ sink->audioflinger_device = NULL;
+ sink->m_volume = DEFAULT_VOLUME;
+ sink->m_mute = DEFAULT_MUTE;
+ sink->m_init = FALSE;
+ sink->m_audiosink = NULL;
+ sink->eos = FALSE;
+ sink->may_provide_clock = TRUE;
+ sink->last_resync_sample = -1;
+
+ if (sink->system_clock) {
+ gst_clock_set_master (sink->system_clock, NULL);
+ gst_object_replace ((GstObject **) & sink->system_clock, NULL);
+ gst_object_replace ((GstObject **) & sink->system_audio_clock, NULL);
+ }
+
+ if (sink->audio_clock)
+ gst_object_replace ((GstObject **) & sink->audio_clock, NULL);
+
+ if (sink->exported_clock)
+ gst_object_replace ((GstObject **) & sink->exported_clock, NULL);
+
+ if (create_clocks) {
+ GstClockTime external, internal;
+
+ /* create the audio clock that uses the ringbuffer as its audio source */
+ sink->audio_clock = gst_audio_clock_new ("GstAudioFlingerSinkClock",
+ gst_audioflinger_sink_get_time, sink);
+
+ /* always set audio_clock as baseaudiosink's provided_clock */
+ gst_object_replace ((GstObject **) &
+ GST_BASE_AUDIO_SINK (sink)->provided_clock,
+ GST_OBJECT (sink->audio_clock));
+
+ /* create the system_audio_clock, which is an *audio clock* that uses an
+ * instance of the system clock as its time source */
+ sink->system_audio_clock =
+ gst_audio_clock_new ("GstAudioFlingerSystemAudioClock",
+ gst_audioflinger_sink_system_audio_clock_get_time, sink);
+
+ /* create an instance of the system clock, that we slave to
+ * sink->audio_clock to have an audio clock with an higher resolution than
+ * the segment size (50ms) */
+ sink->system_clock = g_object_new (GST_TYPE_SYSTEM_CLOCK,
+ "name", "GstAudioFlingerSystemClock", NULL);
+
+ /* calibrate the clocks */
+ external = gst_clock_get_time (sink->audio_clock);
+ internal = gst_clock_get_internal_time (sink->system_clock);
+ gst_clock_set_calibration (sink->system_clock, internal, external, 1, 1);
+
+ /* slave the system clock to the audio clock */
+ GST_OBJECT_FLAG_SET (sink->system_clock, GST_CLOCK_FLAG_CAN_SET_MASTER);
+ g_object_set (sink->system_clock, "timeout", 50 * GST_MSECOND, NULL);
+ gst_clock_set_master (sink->system_clock, sink->audio_clock);
+ }
+
+}
+
+static void
+gst_audioflinger_sink_finalise (GObject * object)
+{
+ GstAudioFlingerSink *audioflinger_sink = GST_AUDIOFLINGERSINK (object);
+
+ GST_INFO_OBJECT (object, "finalize");
+
+ gst_audioflinger_sink_reset (audioflinger_sink, FALSE);
+
+ G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (object));
+}
+
+static GstRingBuffer *
+gst_audioflinger_sink_create_ringbuffer (GstBaseAudioSink * sink)
+{
+ GstRingBuffer *buffer;
+
+ GST_DEBUG_OBJECT (sink, "creating ringbuffer");
+ LOGD ("creating ringbuffer");
+ buffer = g_object_new (GST_TYPE_ANDROID_AUDIORING_BUFFER, NULL);
+ GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer);
+ LOGD ("created ringbuffer @%p", buffer);
+
+ return buffer;
+}
+
+static void
+gst_audioflinger_sink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstAudioFlingerSink *audioflinger_sink;
+
+ audioflinger_sink = GST_AUDIOFLINGERSINK (object);
+ g_return_if_fail (audioflinger_sink != NULL);
+
+ switch (prop_id) {
+ case PROP_MUTE:
+ g_value_set_boolean (value, audioflinger_sink->m_mute);
+ GST_DEBUG_OBJECT (audioflinger_sink, "get mute: %d",
+ audioflinger_sink->m_mute);
+ break;
+ case PROP_VOLUME:
+ g_value_set_double (value, audioflinger_sink->m_volume);
+ GST_DEBUG_OBJECT (audioflinger_sink, "get volume: %f",
+ audioflinger_sink->m_volume);
+ break;
+ case PROP_AUDIO_SINK:
+ GST_ERROR_OBJECT (audioflinger_sink, "Shall not go here!");
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_audioflinger_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstAudioFlingerSink *audioflinger_sink;
+ audioflinger_sink = GST_AUDIOFLINGERSINK (object);
+
+ g_return_if_fail (audioflinger_sink != NULL);
+ GST_OBJECT_LOCK (audioflinger_sink);
+ switch (prop_id) {
+ case PROP_MUTE:
+ audioflinger_sink->m_mute = g_value_get_boolean (value);
+ GST_DEBUG_OBJECT (audioflinger_sink, "set mute: %d",
+ audioflinger_sink->m_mute);
+ /* set device if it's initialized */
+ if (audioflinger_sink->audioflinger_device && audioflinger_sink->m_init)
+ gst_audioflinger_sink_set_mute (audioflinger_sink,
+ (int) (audioflinger_sink->m_mute));
+ break;
+ case PROP_VOLUME:
+ audioflinger_sink->m_volume = g_value_get_double (value);
+ GST_DEBUG_OBJECT (audioflinger_sink, "set volume: %f",
+ audioflinger_sink->m_volume);
+ /* set device if it's initialized */
+ if (audioflinger_sink->audioflinger_device && audioflinger_sink->m_init)
+ gst_audioflinger_sink_set_volume (audioflinger_sink,
+ (float) audioflinger_sink->m_volume);
+ break;
+ case PROP_AUDIO_SINK:
+ audioflinger_sink->m_audiosink = g_value_get_pointer (value);
+ GST_DEBUG_OBJECT (audioflinger_sink, "set audiosink: %p",
+ audioflinger_sink->m_audiosink);
+ LOGD ("set audiosink: %p", audioflinger_sink->m_audiosink);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ GST_OBJECT_UNLOCK (audioflinger_sink);
+}
+
+static GstCaps *
+gst_audioflinger_sink_getcaps (GstBaseSink * bsink)
+{
+ GstAudioFlingerSink *audioflinger_sink;
+ GstCaps *caps;
+
+ audioflinger_sink = GST_AUDIOFLINGERSINK (bsink);
+ GST_DEBUG_OBJECT (audioflinger_sink, "enter,%p",
+ audioflinger_sink->audioflinger_device);
+ LOGD ("gst_audioflinger_sink_getcaps,%p",
+ audioflinger_sink->audioflinger_device);
+ if (audioflinger_sink->audioflinger_device == NULL
+ || audioflinger_sink->m_init == FALSE) {
+ caps =
+ gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
+ (bsink)));
+ } else if (audioflinger_sink->probed_caps) {
+ caps = gst_caps_copy (audioflinger_sink->probed_caps);
+ } else {
+ caps = gst_caps_new_any ();
+ if (caps && !gst_caps_is_empty (caps)) {
+ audioflinger_sink->probed_caps = gst_caps_copy (caps);
+ }
+ }
+
+ return caps;
+}
+
+static gboolean
+gst_audioflinger_sink_open (GstAudioFlingerSink * audioflinger)
+{
+ GstBaseAudioSink *baseaudiosink = (GstBaseAudioSink *) audioflinger;
+
+ GST_DEBUG_OBJECT (audioflinger, "enter");
+ LOGV ("gst_audioflinger_sink_open");
+ g_return_val_if_fail (audioflinger != NULL, FALSE);
+
+ baseaudiosink->buffer_time = DEFAULT_BUFFERTIME;
+ baseaudiosink->latency_time = DEFAULT_LATENCYTIME;
+
+ if (audioflinger->audioflinger_device == NULL) {
+ if (audioflinger->m_audiosink) {
+ if (!(audioflinger->audioflinger_device =
+ audioflinger_device_open (audioflinger->m_audiosink)))
+ goto failed_creation;
+ GST_DEBUG_OBJECT (audioflinger, "open an existed flinger, %p",
+ audioflinger->audioflinger_device);
+ LOGD ("open an existed flinger, %p", audioflinger->audioflinger_device);
+ } else {
+ if (!(audioflinger->audioflinger_device = audioflinger_device_create ()))
+ goto failed_creation;
+ GST_DEBUG_OBJECT (audioflinger, "create a new flinger, %p",
+ audioflinger->audioflinger_device);
+ LOGD ("create a new flinger, %p", audioflinger->audioflinger_device);
+ }
+ }
+ return TRUE;
+
+ /* ERRORS */
+failed_creation:
+ {
+ GST_ELEMENT_ERROR (audioflinger, RESOURCE, SETTINGS, (NULL),
+ ("Failed to create AudioFlinger"));
+ LOGE ("Failed to create AudioFlinger");
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_audioflinger_sink_close (GstAudioFlingerSink * audioflinger)
+{
+ GST_DEBUG_OBJECT (audioflinger, "enter");
+ LOGD ("gst_audioflinger_sink_close");
+
+ if (audioflinger->audioflinger_device != NULL) {
+ GST_DEBUG_OBJECT (audioflinger, "release flinger device");
+ LOGD ("release flinger device");
+ audioflinger_device_stop (audioflinger->audioflinger_device);
+ audioflinger_device_release (audioflinger->audioflinger_device);
+ audioflinger->audioflinger_device = NULL;
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_audioflinger_sink_prepare (GstAudioFlingerSink * audioflinger,
+ GstRingBufferSpec * spec)
+{
+ GST_DEBUG_OBJECT (audioflinger, "enter");
+ LOGD ("gst_audioflinger_sink_prepare");
+
+ /* FIXME:
+ *
+ * Pipeline crashes in audioflinger_device_set(), after releasing audio
+ * flinger device and creating it again. In most cases, it will happen when
+ * playing the same audio again.
+ *
+ * It seems the root cause is we create and release audio flinger sink in
+ * different thread in playbin2. Till now, I haven't found way to
+ * create/release device in the same thread. Fortunately, it will not effect
+ * the gst-launch usage
+ */
+ if (audioflinger_device_set (audioflinger->audioflinger_device,
+ 3, spec->channels, spec->rate, spec->segsize) == -1)
+ goto failed_creation;
+
+ audioflinger->m_init = TRUE;
+ spec->bytes_per_sample = (spec->width / 8) * spec->channels;
+ audioflinger->bytes_per_sample = spec->bytes_per_sample;
+
+ spec->segsize =
+ audioflinger_device_frameCount (audioflinger->audioflinger_device) *
+ spec->bytes_per_sample;
+
+ GST_DEBUG_OBJECT (audioflinger,
+ "channels: %d, rate: %d, width: %d, got segsize: %d, segtotal: %d, "
+ "frame count: %d, frame size: %d",
+ spec->channels, spec->rate, spec->width, spec->segsize, spec->segtotal,
+ audioflinger_device_frameCount (audioflinger->audioflinger_device),
+ audioflinger_device_frameSize (audioflinger->audioflinger_device)
+ );
+ LOGD ("channels: %d, rate: %d, width: %d, got segsize: %d, segtotal: %d, "
+ "frame count: %d, frame size: %d",
+ spec->channels, spec->rate, spec->width, spec->segsize, spec->segtotal,
+ audioflinger_device_frameCount (audioflinger->audioflinger_device),
+ audioflinger_device_frameSize (audioflinger->audioflinger_device)
+ );
+
+#if 0
+ GST_DEBUG_OBJECT (audioflinger, "pause device");
+ LOGV ("pause device");
+ audioflinger_device_pause (audioflinger->audioflinger_device);
+#endif
+
+ return TRUE;
+
+ /* ERRORS */
+failed_creation:
+ {
+ GST_ELEMENT_ERROR (audioflinger, RESOURCE, SETTINGS, (NULL),
+ ("Failed to create AudioFlinger for format %d", spec->format));
+ LOGE ("Failed to create AudioFlinger for format %d", spec->format);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_audioflinger_sink_unprepare (GstAudioFlingerSink * audioflinger)
+{
+ GST_DEBUG_OBJECT (audioflinger, "enter");
+ LOGD ("gst_audioflinger_sink_unprepare");
+
+ if (audioflinger->audioflinger_device != NULL) {
+ GST_DEBUG_OBJECT (audioflinger, "release flinger device");
+ LOGD ("release flinger device");
+ audioflinger_device_stop (audioflinger->audioflinger_device);
+ audioflinger->m_init = FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gst_audioflinger_sink_set_mute (GstAudioFlingerSink * audioflinger_sink,
+ gboolean mute)
+{
+ GST_DEBUG_OBJECT (audioflinger_sink, "set PROP_MUTE = %d\n", mute);
+ LOGD ("set PROP_MUTE = %d\n", mute);
+
+ if (audioflinger_sink->audioflinger_device)
+ audioflinger_device_mute (audioflinger_sink->audioflinger_device, mute);
+ audioflinger_sink->m_mute = mute;
+}
+
+static void
+gst_audioflinger_sink_set_volume (GstAudioFlingerSink * audioflinger_sink,
+ float volume)
+{
+ GST_DEBUG_OBJECT (audioflinger_sink, "set PROP_VOLUME = %f\n", volume);
+ LOGD ("set PROP_VOLUME = %f\n", volume);
+
+ if (audioflinger_sink->audioflinger_device != NULL) {
+ audioflinger_device_set_volume (audioflinger_sink->audioflinger_device,
+ volume, volume);
+ }
+}
+
+gboolean
+gst_audioflinger_sink_plugin_init (GstPlugin * plugin)
+{
+ return gst_element_register (plugin, "audioflingersink", GST_RANK_PRIMARY,
+ GST_TYPE_AUDIOFLINGERSINK);
+}
+
+/*
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "audioflingersink",
+ "audioflinger sink audio", plugin_init, VERSION, "LGPL", "GStreamer",
+ "http://gstreamer.net/")
+ */
+
+static GstClock *
+gst_audioflinger_sink_provide_clock (GstElement * elem)
+{
+ GstBaseAudioSink *sink;
+ GstAudioFlingerSink *asink;
+ GstClock *clock;
+
+ sink = GST_BASE_AUDIO_SINK (elem);
+ asink = GST_AUDIOFLINGERSINK (elem);
+
+ /* we have no ringbuffer (must be NULL state) */
+ if (sink->ringbuffer == NULL)
+ goto wrong_state;
+
+ if (!gst_ring_buffer_is_acquired (sink->ringbuffer))
+ goto wrong_state;
+
+ GST_OBJECT_LOCK (sink);
+ if (!asink->may_provide_clock)
+ goto already_playing;
+
+ if (!sink->provide_clock)
+ goto clock_disabled;
+
+ clock = GST_CLOCK_CAST (gst_object_ref (asink->exported_clock));
+ GST_INFO_OBJECT (asink, "providing clock %p %s", clock,
+ clock == NULL ? NULL : GST_OBJECT_NAME (clock));
+ GST_OBJECT_UNLOCK (sink);
+
+ return clock;
+
+ /* ERRORS */
+wrong_state:
+ {
+ GST_DEBUG_OBJECT (sink, "ringbuffer not acquired");
+ return NULL;
+ }
+already_playing:
+ {
+ GST_INFO_OBJECT (sink, "we went to playing already");
+ GST_OBJECT_UNLOCK (sink);
+ return NULL;
+ }
+clock_disabled:
+ {
+ GST_DEBUG_OBJECT (sink, "clock provide disabled");
+ GST_OBJECT_UNLOCK (sink);
+ return NULL;
+ }
+}
+
+static GstStateChangeReturn
+gst_audioflinger_sink_change_state (GstElement * element,
+ GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ GstClockTime time;
+ GstAudioFlingerSink *sink = GST_AUDIOFLINGERSINK (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ sink->may_provide_clock = FALSE;
+ if (sink->exported_clock == sink->system_audio_clock) {
+ GstClockTime cinternal, cexternal, crate_num, crate_denom;
+ GstClockTime before;
+
+ /* take the slave lock to make sure that the slave_callback doesn't run
+ * while we're moving sink->audio_clock forward, causing
+ * sink->system_clock to jump as well */
+ GST_CLOCK_SLAVE_LOCK (sink->system_clock);
+ before = gst_clock_get_time (sink->audio_clock);
+ gst_clock_get_calibration (sink->audio_clock, NULL, NULL,
+ &crate_num, &crate_denom);
+ cinternal = gst_clock_get_internal_time (sink->audio_clock);
+ cexternal = gst_clock_get_time (GST_ELEMENT_CLOCK (sink));
+ gst_clock_set_calibration (sink->audio_clock, cinternal, cexternal,
+ crate_num, crate_denom);
+ /* reset observations */
+ sink->system_clock->filling = TRUE;
+ sink->system_clock->time_index = 0;
+ GST_CLOCK_SLAVE_UNLOCK (sink->system_clock);
+
+ time = gst_clock_get_time (sink->audio_clock);
+ GST_INFO_OBJECT (sink, "PAUSED_TO_PLAYING,"
+ " base_time %" GST_TIME_FORMAT
+ " before %" GST_TIME_FORMAT " after %" GST_TIME_FORMAT
+ " internal %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GST_ELEMENT (sink)->base_time),
+ GST_TIME_ARGS (before), GST_TIME_ARGS (time),
+ GST_TIME_ARGS (cinternal), GST_TIME_ARGS (cexternal));
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static GstFlowReturn
+gst_audioflinger_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
+{
+ GstFlowReturn ret;
+ gboolean us_live = FALSE;
+ GstQuery *query;
+ GstAudioFlingerSink *asink = GST_AUDIOFLINGERSINK (bsink);
+ GstClock *clock;
+
+ GST_INFO_OBJECT (bsink, "preroll");
+
+ ret = GST_BASE_SINK_CLASS (parent_class)->preroll (bsink, buffer);
+ if (ret != GST_FLOW_OK)
+ goto done;
+
+ if (asink->exported_clock != NULL) {
+ GST_INFO_OBJECT (bsink, "clock already exported");
+ goto done;
+ }
+
+ query = gst_query_new_latency ();
+
+ /* ask the peer for the latency */
+ if (gst_pad_peer_query (bsink->sinkpad, query)) {
+ /* get upstream min and max latency */
+ gst_query_parse_latency (query, &us_live, NULL, NULL);
+ GST_INFO_OBJECT (bsink, "query result live: %d", us_live);
+ } else {
+ GST_WARNING_OBJECT (bsink, "latency query failed");
+ }
+ gst_query_unref (query);
+
+ if (!us_live && asink->export_system_audio_clock) {
+ clock = asink->system_audio_clock;
+ /* set SLAVE_NONE so that baseaudiosink doesn't try to slave audio_clock to
+ * system_audio_clock
+ */
+ g_object_set (asink, "slave-method", GST_BASE_AUDIO_SINK_SLAVE_NONE, NULL);
+ } else {
+ clock = asink->audio_clock;
+ }
+
+ GST_INFO_OBJECT (bsink, "using %s clock",
+ clock == asink->audio_clock ? "audio" : "system_audio");
+ gst_object_replace ((GstObject **) & asink->exported_clock,
+ GST_OBJECT (clock));
+ GST_OBJECT_UNLOCK (asink);
+
+done:
+ return ret;
+}
+
+static gboolean
+gst_audioflinger_sink_event (GstBaseSink * bsink, GstEvent * event)
+{
+ gboolean res;
+ GstAudioFlingerSink *asink = GST_AUDIOFLINGERSINK (bsink);
+ GstBaseAudioSink *baseaudiosink = GST_BASE_AUDIO_SINK (bsink);
+ GstRingBuffer *ringbuf = baseaudiosink->ringbuffer;
+ const gchar *event_name;
+
+ event_name = GST_EVENT_TYPE_NAME (event);
+ GST_INFO_OBJECT (bsink, "received %s event", event_name);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ GST_INFO_OBJECT (asink, "got EOS");
+ asink->eos = TRUE;
+
+ if (baseaudiosink->next_sample != -1) {
+ guint64 next_sample, sample;
+ gint sps;
+ GstFlowReturn ret;
+
+ sps = ringbuf->spec.segsize / ringbuf->spec.bytes_per_sample;
+ sample = baseaudiosink->next_sample;
+ next_sample = baseaudiosink->next_sample / sps;
+ if (next_sample < ringbuf->spec.segsize) {
+ gint samples, out_samples, accum, size;
+ GstClockTime timestamp, before, after;
+ guchar *data, *data_start;
+ guint written;
+ guint64 offset;
+
+ samples = (ringbuf->spec.segsize - next_sample);
+ size = samples * ringbuf->spec.bytes_per_sample;
+ timestamp = gst_util_uint64_scale_int (baseaudiosink->next_sample,
+ GST_SECOND, ringbuf->spec.rate);
+
+ before = gst_clock_get_internal_time (asink->audio_clock);
+ GST_INFO_OBJECT (asink, "%" G_GINT64_FORMAT " < %d, "
+ "padding with silence, samples %d size %d ts %" GST_TIME_FORMAT,
+ next_sample, ringbuf->spec.segsize, samples, size,
+ GST_TIME_ARGS (timestamp));
+ LOGV ("PADDING");
+
+ data_start = data = g_malloc0 (size);
+ offset = baseaudiosink->next_sample;
+ out_samples = samples;
+
+ GST_INFO_OBJECT (bsink, "about to write");
+ do {
+ written =
+ gst_ring_buffer_commit_full (ringbuf, &offset, data, samples,
+ out_samples, &accum);
+
+ GST_INFO_OBJECT (bsink, "wrote %u of %u", written, samples);
+ /* if we wrote all, we're done */
+ if (written == samples)
+ break;
+
+ /* else something interrupted us and we wait for preroll. */
+ if ((ret = gst_base_sink_wait_preroll (bsink)) != GST_FLOW_OK)
+ break;
+
+ /* update the output samples. FIXME, this will just skip them when pausing
+ * during trick mode */
+ if (out_samples > written) {
+ out_samples -= written;
+ accum = 0;
+ } else
+ break;
+
+ samples -= written;
+ data += written * ringbuf->spec.bytes_per_sample;
+ } while (TRUE);
+
+ g_free (data_start);
+ after = gst_clock_get_internal_time (asink->audio_clock);
+ GST_INFO_OBJECT (asink, "padded, left %d before %" GST_TIME_FORMAT
+ " after %" GST_TIME_FORMAT, samples,
+ GST_TIME_ARGS (before), GST_TIME_ARGS (after));
+ } else {
+ LOGV ("NOT PADDING 1");
+ }
+ } else {
+ LOGV ("NOT PADDING 2");
+ }
+
+ break;
+#if 0
+ case GST_EVENT_BUFFERING_START:
+ GST_INFO_OBJECT (asink, "buffering start");
+ break;
+ case GST_EVENT_BUFFERING_STOP:
+ {
+ gboolean slaved;
+ GstClockTime cinternal, cexternal, crate_num, crate_denom;
+ GstClockTime before, after;
+
+ gst_clock_get_calibration (asink->audio_clock, &cinternal, &cexternal,
+ &crate_num, &crate_denom);
+
+ before = gst_clock_get_time (asink->audio_clock);
+
+ cinternal = gst_clock_get_internal_time (asink->audio_clock);
+ cexternal = gst_clock_get_time (GST_ELEMENT_CLOCK (asink));
+ gst_clock_set_calibration (asink->audio_clock, cinternal,
+ cexternal, crate_num, crate_denom);
+
+ after = gst_clock_get_time (asink->audio_clock);
+
+ GST_INFO_OBJECT (asink, "buffering stopped, clock recalibrated"
+ " before %" GST_TIME_FORMAT " after %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (before), GST_TIME_ARGS (after));
+
+ /* force baseaudiosink to resync from the next buffer */
+ GST_BASE_AUDIO_SINK (asink)->next_sample = -1;
+
+ /* reset this so we allow some time before enabling slaving again */
+ asink->last_resync_sample = -1;
+ slaved = GST_ELEMENT_CLOCK (asink) != asink->exported_clock;
+ if (slaved) {
+ GST_INFO_OBJECT (asink, "disabling slaving");
+ g_object_set (asink, "slave-method", GST_BASE_AUDIO_SINK_SLAVE_NONE,
+ NULL);
+ asink->slaving_disabled = TRUE;
+ }
+
+ g_object_set (asink, "drift-tolerance", 200 * GST_MSECOND, NULL);
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+
+ GST_INFO_OBJECT (bsink, "chaining up event %s", event_name);
+
+ res = GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
+
+ GST_INFO_OBJECT (bsink, "done with event %s", event_name);
+
+ return res;
+}
+
+static GstClockTime
+gst_audioflinger_sink_get_time (GstClock * clock, gpointer user_data)
+{
+ GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (user_data);
+ uint32_t position = -1;
+ GstAudioFlingerSink *asink = GST_AUDIOFLINGERSINK (sink);
+ GstClockTime time = GST_CLOCK_TIME_NONE;
+ GstClockTime pipeline_time = GST_CLOCK_TIME_NONE;
+ GstClockTime system_audio_clock_time = GST_CLOCK_TIME_NONE;
+ GstClockTime offset = GST_CLOCK_TIME_NONE;
+ GstClockTime adjusted_time = GST_CLOCK_TIME_NONE;
+ GstClockTime cinternal, cexternal, crate_num, crate_denom;
+ gint64 latency, mixer_latency;
+
+ gst_clock_get_calibration (clock, &cinternal, &cexternal,
+ &crate_num, &crate_denom);
+
+ if (!asink->audioflinger_device || !asink->m_init) {
+ GST_LOG_OBJECT (sink, "device not created yet");
+
+ goto out;
+ }
+
+ if (!sink->ringbuffer) {
+ GST_DEBUG_OBJECT (sink, "NULL ringbuffer");
+
+ goto out;
+ }
+
+ if (!sink->ringbuffer->acquired) {
+ GST_DEBUG_OBJECT (sink, "ringbuffer not acquired");
+
+ goto out;
+ }
+
+ position = audioflinger_device_get_position (asink->audioflinger_device);
+ if (position == -1)
+ goto out;
+
+ /* AudioTrack::latency returns the audioflinger latency + the sw mixer latency.
+ * The mixer latency doesn't apply here since we get clock values "after"
+ * that latency. We just need to get the audioflinger latency.
+ *
+ * see AudioTrack::set and gst_base_audio_sink_setcaps to understand how
+ * latency and mixer_latency are computed.
+ */
+ latency =
+ audioflinger_device_latency (asink->audioflinger_device) * GST_MSECOND;
+ mixer_latency = sink->ringbuffer->spec.latency_time * GST_USECOND;
+ latency -= mixer_latency;
+
+ time = gst_util_uint64_scale_int (position, GST_SECOND,
+ sink->ringbuffer->spec.rate);
+
+ if (time >= latency)
+ time -= latency;
+ else
+ time = 0;
+
+ offset = gst_audio_clock_adjust (GST_CLOCK (clock), 0);
+ adjusted_time = gst_audio_clock_adjust (GST_CLOCK (clock), time);
+
+ if (asink->system_audio_clock)
+ system_audio_clock_time = gst_clock_get_time (asink->system_audio_clock);
+
+ if (GST_ELEMENT_CLOCK (asink)
+ && asink->audio_clock != GST_ELEMENT_CLOCK (asink))
+ pipeline_time = gst_clock_get_time (GST_ELEMENT_CLOCK (asink));
+
+out:
+
+ GST_LOG_OBJECT (sink,
+ "processed samples %" G_GINT32_FORMAT
+ " time %" GST_TIME_FORMAT
+ " latency %" GST_TIME_FORMAT
+ " pipeline time %" GST_TIME_FORMAT
+ " system_audio_clock %" GST_TIME_FORMAT
+ " adjusted_time %" GST_TIME_FORMAT
+ " cinternal %" GST_TIME_FORMAT
+ " cexternal %" GST_TIME_FORMAT,
+ position, GST_TIME_ARGS (time), GST_TIME_ARGS (latency),
+ GST_TIME_ARGS (pipeline_time), GST_TIME_ARGS (system_audio_clock_time),
+ GST_TIME_ARGS (adjusted_time), GST_TIME_ARGS (cinternal),
+ GST_TIME_ARGS (cexternal));
+
+ return time;
+}
+
+static GstClockTime
+gst_audioflinger_sink_system_audio_clock_get_time (GstClock * clock,
+ gpointer user_data)
+{
+ GstClockTime time, offset;
+ GstAudioFlingerSink *sink = GST_AUDIOFLINGERSINK (user_data);
+
+ time = gst_clock_get_time (sink->system_clock);
+ offset = gst_audio_clock_adjust (clock, (GstClockTime) 0);
+ time -= offset;
+
+ return time;
+}
diff --git a/sink/audioflingersink/gstaudioflingersink.h b/sink/audioflingersink/gstaudioflingersink.h
new file mode 100644
index 0000000..646459d
--- /dev/null
+++ b/sink/audioflingersink/gstaudioflingersink.h
@@ -0,0 +1,70 @@
+/* GStreamer
+ * Copyright (C) <2009> Prajnashi S <prajnashi@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GST_AUDIOFLINGERSINK_H__
+#define __GST_AUDIOFLINGERSINK_H__
+
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiosink.h>
+#include "audioflinger_wrapper.h"
+
+
+G_BEGIN_DECLS
+#define GST_TYPE_AUDIOFLINGERSINK (gst_audioflinger_sink_get_type())
+#define GST_AUDIOFLINGERSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIOFLINGERSINK,GstAudioFlingerSink))
+#define GST_AUDIOFLINGERSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIOFLINGERSINK,GstAudioFlingerSinkClass))
+#define GST_IS_AUDIOFLINGERSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIOFLINGERSINK))
+#define GST_IS_AUDIOFLINGERSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIOFLINGERSINK))
+typedef struct _GstAudioFlingerSink GstAudioFlingerSink;
+typedef struct _GstAudioFlingerSinkClass GstAudioFlingerSinkClass;
+
+struct _GstAudioFlingerSink
+{
+ GstAudioSink sink;
+
+ AudioFlingerDeviceHandle audioflinger_device;
+ gboolean m_init;
+ gint bytes_per_sample;
+ gdouble m_volume;
+ gboolean m_mute;
+ gpointer m_audiosink;
+ GstCaps *probed_caps;
+ gboolean eos;
+ GstClock *audio_clock;
+ GstClock *system_clock;
+ GstClock *system_audio_clock;
+ GstClock *exported_clock;
+ gboolean export_system_audio_clock;
+ gboolean may_provide_clock;
+ gboolean slaving_disabled;
+ guint64 last_resync_sample;
+ guint64 flush_error_offset;
+};
+
+struct _GstAudioFlingerSinkClass
+{
+ GstAudioSinkClass parent_class;
+};
+
+GType gst_audioflinger_sink_get_type (void);
+
+gboolean gst_audioflinger_sink_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+#endif /* __GST_AUDIOFLINGERSINK_H__ */
diff --git a/sink/surfaceflingersink/Android.mk b/sink/surfaceflingersink/Android.mk
new file mode 100644
index 0000000..f19017a
--- /dev/null
+++ b/sink/surfaceflingersink/Android.mk
@@ -0,0 +1,40 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ surfaceflinger_wrap.cpp \
+ gstsurfaceflingersink.c
+
+LOCAL_SHARED_LIBRARIES := \
+ libgstreamer-0.10 \
+ libglib-2.0 \
+ libgthread-2.0 \
+ libgmodule-2.0 \
+ libgobject-2.0 \
+ libgstbase-0.10 \
+ libgstvideo-0.10 \
+ libcutils \
+ libutils \
+ libui \
+ libsurfaceflinger \
+ libsurfaceflinger_client \
+ libbinder
+
+LOCAL_MODULE:= libgstsurfaceflingersink
+
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/../../ \
+ frameworks/base/include
+
+LOCAL_CFLAGS := -DANDROID_USE_GSTREAMER \
+ -DHAVE_CONFIG_H \
+ $(shell $(PKG_CONFIG) gstreamer-video-0.10 --cflags)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/gstreamer-0.10
+LOCAL_PRELINK_MODULE := false
+LOCAL_MODULE_TAGS := eng debug
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/sink/surfaceflingersink/gstsurfaceflingersink.c b/sink/surfaceflingersink/gstsurfaceflingersink.c
new file mode 100644
index 0000000..479a884
--- /dev/null
+++ b/sink/surfaceflingersink/gstsurfaceflingersink.c
@@ -0,0 +1,386 @@
+/* GStreamer
+ * Copyright (C) <2009> Prajnashi S <prajnashi@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+
+#include "gstsurfaceflingersink.h"
+
+GST_DEBUG_CATEGORY (gst_surfaceflinger_sink_debug);
+#define GST_CAT_DEFAULT gst_surfaceflinger_sink_debug
+
+/* elementfactory information */
+static const GstElementDetails gst_surfaceflinger_sink_details =
+GST_ELEMENT_DETAILS ("android's surface flinger sink",
+ "Sink/Video",
+ "A linux framebuffer videosink",
+ "Prajnashi S <prajnashi@gmail.com>");
+
+enum
+{
+ ARG_0,
+ PROP_SURFACE,
+};
+
+static void gst_surfaceflinger_sink_base_init (gpointer g_class);
+static void gst_surfaceflinger_sink_class_init (GstSurfaceFlingerSinkClass *
+ klass);
+static void gst_surfaceflinger_sink_get_times (GstBaseSink * basesink,
+ GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
+
+static gboolean gst_surfaceflinger_sink_setcaps (GstBaseSink * bsink,
+ GstCaps * caps);
+
+static GstFlowReturn gst_surfaceflinger_sink_render (GstBaseSink * bsink,
+ GstBuffer * buff);
+static gboolean gst_surfaceflinger_sink_start (GstBaseSink * bsink);
+static gboolean gst_surfaceflinger_sink_stop (GstBaseSink * bsink);
+
+static void gst_surfaceflinger_sink_init (GstSurfaceFlingerSink * surfacesink);
+static void gst_surfaceflinger_sink_finalize (GObject * object);
+static void gst_surfaceflinger_sink_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_surfaceflinger_sink_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+static GstStateChangeReturn
+gst_surfaceflinger_sink_change_state (GstElement * element,
+ GstStateChange transition);
+
+static GstCaps *gst_surfaceflinger_sink_getcaps (GstBaseSink * bsink);
+
+static GstVideoSinkClass *parent_class = NULL;
+
+/* TODO: support more pixel form in the future */
+#define GST_SURFACE_TEMPLATE_CAPS GST_VIDEO_CAPS_RGB_16
+
+static void
+gst_surfaceflinger_sink_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_SURFACE_TEMPLATE_CAPS)
+ );
+
+ gst_element_class_set_details (element_class,
+ &gst_surfaceflinger_sink_details);
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sink_template));
+}
+
+
+static void
+gst_surfaceflinger_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end)
+{
+ GstSurfaceFlingerSink *surfacesink;
+
+ surfacesink = GST_SURFACEFLINGERSINK (basesink);
+
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
+ *start = GST_BUFFER_TIMESTAMP (buffer);
+ if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
+ *end = *start + GST_BUFFER_DURATION (buffer);
+ } else {
+ if (surfacesink->fps_n > 0) {
+ *end = *start +
+ gst_util_uint64_scale_int (GST_SECOND, surfacesink->fps_d,
+ surfacesink->fps_n);
+ }
+ }
+ }
+}
+
+static GstCaps *
+gst_surfaceflinger_sink_getcaps (GstBaseSink * bsink)
+{
+ GstSurfaceFlingerSink *surfacesink;
+ GstCaps *caps;
+
+ surfacesink = GST_SURFACEFLINGERSINK (bsink);
+ caps = gst_caps_from_string (GST_SURFACE_TEMPLATE_CAPS);
+
+ return caps;
+}
+
+static gboolean
+gst_surfaceflinger_sink_setcaps (GstBaseSink * bsink, GstCaps * vscapslist)
+{
+ GstSurfaceFlingerSink *surfacesink;
+ GstStructure *structure;
+ const GValue *fps;
+
+ surfacesink = GST_SURFACEFLINGERSINK (bsink);
+
+ GST_DEBUG_OBJECT (surfacesink, "caps after linked: %s",
+ gst_caps_to_string (vscapslist));
+
+ structure = gst_caps_get_structure (vscapslist, 0);
+
+ surfacesink->pixel_format = VIDEO_FLINGER_RGB_565;
+ fps = gst_structure_get_value (structure, "framerate");
+ surfacesink->fps_n = gst_value_get_fraction_numerator (fps);
+ surfacesink->fps_d = gst_value_get_fraction_denominator (fps);
+
+ gst_structure_get_int (structure, "width", &surfacesink->width);
+ gst_structure_get_int (structure, "height", &surfacesink->height);
+
+ GST_DEBUG_OBJECT (surfacesink, "framerate=%d/%d", surfacesink->fps_n,
+ surfacesink->fps_d);
+ GST_DEBUG_OBJECT (surfacesink,
+ "register framebuffers: width=%d, height=%d, pixel_format=%d",
+ surfacesink->width, surfacesink->height, surfacesink->pixel_format);
+
+ /* register frame buffer */
+ videoflinger_device_register_framebuffers (surfacesink->videodev,
+ surfacesink->width, surfacesink->height, surfacesink->pixel_format);
+
+ GST_DEBUG_OBJECT (surfacesink, "gst_surfaceflinger_sink_setcaps return true");
+ return TRUE;
+}
+
+
+static GstFlowReturn
+gst_surfaceflinger_sink_render (GstBaseSink * bsink, GstBuffer * buf)
+{
+ GstSurfaceFlingerSink *surfacesink;
+
+ surfacesink = GST_SURFACEFLINGERSINK (bsink);
+ GST_DEBUG_OBJECT (surfacesink, "post buffer=%p, size=%d",
+ GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+
+ /* post frame buffer */
+ videoflinger_device_post (surfacesink->videodev, GST_BUFFER_DATA (buf),
+ GST_BUFFER_SIZE (buf));
+
+ GST_DEBUG_OBJECT (surfacesink,
+ "gst_surfaceflinger_sink_render return GST_FLOW_OK");
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_surfaceflinger_sink_start (GstBaseSink * bsink)
+{
+ GstSurfaceFlingerSink *surfacesink;
+
+ surfacesink = GST_SURFACEFLINGERSINK (bsink);
+
+ /* release previous video device */
+ if (surfacesink->videodev != NULL) {
+ videoflinger_device_release (surfacesink->videodev);
+ surfacesink->videodev = NULL;
+ }
+
+ /* create a new video device */
+ GST_DEBUG_OBJECT (surfacesink, "ISurface = %p", surfacesink->isurface);
+ surfacesink->videodev = videoflinger_device_create (surfacesink->isurface);
+ if (surfacesink->videodev == NULL) {
+ GST_ERROR_OBJECT (surfacesink, "Failed to create video device.");
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (surfacesink, "gst_surfaceflinger_sink_start return TRUE");
+ return TRUE;
+}
+
+static gboolean
+gst_surfaceflinger_sink_stop (GstBaseSink * bsink)
+{
+ GstSurfaceFlingerSink *surfacesink;
+
+ surfacesink = GST_SURFACEFLINGERSINK (bsink);
+
+ if (surfacesink->videodev != NULL) {
+ videoflinger_device_release (surfacesink->videodev);
+ surfacesink->videodev = NULL;
+ }
+
+ return TRUE;
+}
+
+static void
+gst_surfaceflinger_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstSurfaceFlingerSink *surfacesink;
+
+ surfacesink = GST_SURFACEFLINGERSINK (object);
+
+ switch (prop_id) {
+ case PROP_SURFACE:
+ surfacesink->isurface = g_value_get_pointer (value);
+ GST_DEBUG_OBJECT (surfacesink, "set property: ISureface = %p",
+ surfacesink->isurface);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_surfaceflinger_sink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstSurfaceFlingerSink *surfacesink;
+
+ surfacesink = GST_SURFACEFLINGERSINK (object);
+
+ switch (prop_id) {
+ case PROP_SURFACE:
+ g_value_set_pointer (value, surfacesink->isurface);
+ GST_DEBUG_OBJECT (surfacesink, "get property: ISurface = %p.",
+ surfacesink->isurface);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* TODO: delete me later */
+static GstStateChangeReturn
+gst_surfaceflinger_sink_change_state (GstElement * element,
+ GstStateChange transition)
+{
+ GstSurfaceFlingerSink *surfacesink;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ g_return_val_if_fail (GST_IS_SURFACEFLINGERSINK (element),
+ GST_STATE_CHANGE_FAILURE);
+ surfacesink = GST_SURFACEFLINGERSINK (element);
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ default:
+ break;
+ }
+ return ret;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (gst_surfaceflinger_sink_debug, "surfaceflingersink",
+ 0, "Video sink plugin");
+
+ if (!gst_element_register (plugin, "surfaceflingersink", GST_RANK_NONE,
+ GST_TYPE_SURFACEFLINGERSINK)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gst_surfaceflinger_sink_class_init (GstSurfaceFlingerSinkClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseSinkClass *gstvs_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstvs_class = (GstBaseSinkClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = gst_surfaceflinger_sink_set_property;
+ gobject_class->get_property = gst_surfaceflinger_sink_get_property;
+ gobject_class->finalize = gst_surfaceflinger_sink_finalize;
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_surfaceflinger_sink_change_state);
+
+ g_object_class_install_property (gobject_class, PROP_SURFACE,
+ g_param_spec_pointer ("surface", "Surface",
+ "The pointer of ISurface interface", G_PARAM_READWRITE));
+
+ gstvs_class->set_caps = GST_DEBUG_FUNCPTR (gst_surfaceflinger_sink_setcaps);
+ gstvs_class->get_caps = GST_DEBUG_FUNCPTR (gst_surfaceflinger_sink_getcaps);
+ gstvs_class->get_times =
+ GST_DEBUG_FUNCPTR (gst_surfaceflinger_sink_get_times);
+ gstvs_class->preroll = GST_DEBUG_FUNCPTR (gst_surfaceflinger_sink_render);
+ gstvs_class->render = GST_DEBUG_FUNCPTR (gst_surfaceflinger_sink_render);
+ gstvs_class->start = GST_DEBUG_FUNCPTR (gst_surfaceflinger_sink_start);
+ gstvs_class->stop = GST_DEBUG_FUNCPTR (gst_surfaceflinger_sink_stop);
+}
+
+static void
+gst_surfaceflinger_sink_init (GstSurfaceFlingerSink * surfacesink)
+{
+ surfacesink->isurface = NULL;
+ surfacesink->videodev = NULL;
+
+ surfacesink->fps_n = 0;
+ surfacesink->fps_d = 1;
+ surfacesink->width = 320;
+ surfacesink->height = 240;
+ surfacesink->pixel_format = -1;
+}
+
+static void
+gst_surfaceflinger_sink_finalize (GObject * object)
+{
+ GstSurfaceFlingerSink *surfacesink = GST_SURFACEFLINGERSINK (object);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+GType
+gst_surfaceflinger_sink_get_type (void)
+{
+ static GType surfacesink_type = 0;
+
+ if (!surfacesink_type) {
+ static const GTypeInfo surfacesink_info = {
+ sizeof (GstSurfaceFlingerSinkClass),
+ gst_surfaceflinger_sink_base_init,
+ NULL,
+ (GClassInitFunc) gst_surfaceflinger_sink_class_init,
+ NULL,
+ NULL,
+ sizeof (GstSurfaceFlingerSink),
+ 0,
+ (GInstanceInitFunc) gst_surfaceflinger_sink_init,
+ };
+
+ surfacesink_type =
+ g_type_register_static (GST_TYPE_BASE_SINK, "GstSurfaceFlingerSink",
+ &surfacesink_info, 0);
+ }
+ return surfacesink_type;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
+ "surfaceflingersink", "android surface flinger sink",
+ plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
diff --git a/sink/surfaceflingersink/gstsurfaceflingersink.h b/sink/surfaceflingersink/gstsurfaceflingersink.h
new file mode 100644
index 0000000..852e6cf
--- /dev/null
+++ b/sink/surfaceflingersink/gstsurfaceflingersink.h
@@ -0,0 +1,60 @@
+
+/* GStreamer
+ * Copyright (C) <2009> Prajnashi S <prajnashi@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GST_FBDEVSINK_H__
+#define __GST_FBDEVSINK_H__
+
+#include <gst/gst.h>
+#include <gst/video/gstvideosink.h>
+#include <gst/video/video.h>
+#include "surfaceflinger_wrap.h"
+
+G_BEGIN_DECLS
+#define GST_TYPE_SURFACEFLINGERSINK \
+ (gst_surfaceflinger_sink_get_type())
+#define GST_SURFACEFLINGERSINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SURFACEFLINGERSINK,GstSurfaceFlingerSink))
+#define GST_SURFACEFLINGERSINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SURFACEFLINGERSINK,GstSurfaceFlingerSinkClass))
+#define GST_IS_SURFACEFLINGERSINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SURFACEFLINGERSINK))
+#define GST_IS_SURFACEFLINGERSINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SURFACEFLINGERSINK))
+typedef struct _GstSurfaceFlingerSink GstSurfaceFlingerSink;
+typedef struct _GstSurfaceFlingerSinkClass GstSurfaceFlingerSinkClass;
+
+struct _GstSurfaceFlingerSink
+{
+ GstVideoSink videosink;
+ gpointer isurface;
+ gint pixel_format;
+ VideoFlingerDeviceHandle videodev;
+ int width, height;
+ int fps_n, fps_d;
+};
+
+struct _GstSurfaceFlingerSinkClass
+{
+ GstBaseSinkClass parent_class;
+};
+
+GType gst_surfaceflinger_sink_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_FBDEVSINK_H__ */
diff --git a/sink/surfaceflingersink/surfaceflinger_wrap.cpp b/sink/surfaceflingersink/surfaceflinger_wrap.cpp
new file mode 100644
index 0000000..e2e98ed
--- /dev/null
+++ b/sink/surfaceflingersink/surfaceflinger_wrap.cpp
@@ -0,0 +1,330 @@
+/* GStreamer
+ * Copyright (C) <2009> Prajnashi S <prajnashi@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+//#define ENABLE_GST_PLAYER_LOG
+#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <cutils/log.h>
+#include "surfaceflinger_wrap.h"
+#include <gst/gst.h>
+
+using namespace android;
+
+typedef struct
+{
+ sp < MemoryHeapBase > frame_heap;
+ sp < ISurface > isurface;
+ sp < SurfaceControl > surface;
+ int32_t hor_stride;
+ int32_t ver_stride;
+ uint32_t width;
+ uint32_t height;
+ PixelFormat format;
+ int frame_offset[2];
+ int buf_index;
+} VideoFlingerDevice;
+
+/* max frame buffers */
+#define MAX_FRAME_BUFFERS 2
+
+static int videoflinger_device_create_new_surface (VideoFlingerDevice *
+ videodev);
+
+/*
+ * The only purpose of class "MediaPlayer" is to call Surface::getISurface()
+ * in frameworks/base/include/ui/Surface.h, which is private function and accessed
+ * by friend class MediaPlayer.
+ *
+ * We define a fake one to cheat compiler
+ */
+namespace android
+{
+ class MediaPlayer
+ {
+ public:
+ static sp < ISurface > getSurface (const Surface * surface)
+ {
+ return surface->getISurface ();
+ };
+ };
+};
+
+
+VideoFlingerDeviceHandle
+videoflinger_device_create (void *isurface)
+{
+ VideoFlingerDevice *videodev = NULL;
+
+ GST_INFO ("Enter\n");
+ videodev = new VideoFlingerDevice;
+ if (videodev == NULL) {
+ return NULL;
+ }
+ videodev->frame_heap.clear ();
+ videodev->isurface = (ISurface *) isurface;
+ videodev->surface.clear ();
+ videodev->format = -1;
+ videodev->width = 0;
+ videodev->height = 0;
+ videodev->hor_stride = 0;
+ videodev->ver_stride = 0;
+ videodev->buf_index = 0;
+ for (int i = 0; i < MAX_FRAME_BUFFERS; i++) {
+ videodev->frame_offset[i] = 0;
+ }
+
+ GST_INFO ("Leave\n");
+ return (VideoFlingerDeviceHandle) videodev;
+}
+
+
+
+int
+videoflinger_device_create_new_surface (VideoFlingerDevice * videodev)
+{
+ status_t state;
+ int pid = getpid ();
+
+ GST_INFO ("Enter\n");
+
+ /* Create a new Surface object with 320x240
+ * TODO: Create surface according to device's screen size and rotate it
+ * 90, but not a pre-defined value.
+ */
+ sp < SurfaceComposerClient > videoClient = new SurfaceComposerClient;
+ if (videoClient.get () == NULL) {
+ GST_ERROR ("Fail to create SurfaceComposerClient\n");
+ return -1;
+ }
+
+ /* release privious surface */
+ videodev->surface.clear ();
+ videodev->isurface.clear ();
+
+ int width = (videodev->width) > 320 ? 320 : videodev->width;
+ int height = (videodev->height) > 320 ? 320 : videodev->height;
+
+ videodev->surface = videoClient->createSurface (pid,
+ 0,
+ width,
+ height,
+ PIXEL_FORMAT_RGB_565,
+ ISurfaceComposer::eFXSurfaceNormal | ISurfaceComposer::ePushBuffers);
+ if (videodev->surface.get () == NULL) {
+ GST_ERROR ("Fail to create Surface\n");
+ return -1;
+ }
+
+ videoClient->openTransaction ();
+
+ /* set Surface toppest z-order, this will bypass all isurface created
+ * in java side and make sure this surface displaied in toppest */
+ state = videodev->surface->setLayer (INT_MAX);
+ if (state != NO_ERROR) {
+ GST_INFO ("videoSurface->setLayer(), state = %d", state);
+ videodev->surface.clear ();
+ return -1;
+ }
+
+ /* show surface */
+ state = videodev->surface->show ();
+ /*state = videodev->surface->setLayer(INT_MAX);
+ if (state != NO_ERROR)
+ {
+ GST_INFO("videoSurface->show(), state = %d", state);
+ videodev->surface.clear();
+ return -1;
+ } */
+
+ /* get ISurface interface */
+ videodev->isurface =
+ MediaPlayer::getSurface (videodev->surface->getSurface ().get ());
+
+ videoClient->closeTransaction ();
+
+ /* Smart pointer videoClient shall be deleted automatically
+ * when function exists
+ */
+ GST_INFO ("Leave\n");
+ return 0;
+}
+
+int
+videoflinger_device_release (VideoFlingerDeviceHandle handle)
+{
+ GST_INFO ("Enter");
+
+ if (handle == NULL) {
+ return -1;
+ }
+
+ /* unregister frame buffer */
+ videoflinger_device_unregister_framebuffers (handle);
+
+ /* release ISurface & Surface */
+ VideoFlingerDevice *videodev = (VideoFlingerDevice *) handle;
+ videodev->isurface.clear ();
+ videodev->surface.clear ();
+
+ /* delete device */
+ delete videodev;
+
+ GST_INFO ("Leave");
+ return 0;
+}
+
+int
+videoflinger_device_register_framebuffers (VideoFlingerDeviceHandle handle,
+ int w, int h, VIDEO_FLINGER_PIXEL_FORMAT format)
+{
+ int surface_format = 0;
+
+ GST_INFO ("Enter");
+ if (handle == NULL) {
+ GST_ERROR ("videodev is NULL");
+ return -1;
+ }
+
+ /* TODO: Now, only PIXEL_FORMAT_RGB_565 is supported. Change here to support
+ * more pixel type
+ */
+ if (format != VIDEO_FLINGER_RGB_565) {
+ GST_ERROR ("Unsupport format: %d", format);
+ return -1;
+ }
+ surface_format = PIXEL_FORMAT_RGB_565;
+
+ VideoFlingerDevice *videodev = (VideoFlingerDevice *) handle;
+ /* unregister previous buffers */
+ if (videodev->frame_heap.get ()) {
+ videoflinger_device_unregister_framebuffers (handle);
+ }
+
+ /* reset framebuffers */
+ videodev->format = surface_format;
+ videodev->width = (w + 1) & -2;
+ videodev->height = (h + 1) & -2;
+ videodev->hor_stride = videodev->width;
+ videodev->ver_stride = videodev->height;
+
+ /* create isurface internally, if no ISurface interface input */
+ if (videodev->isurface.get () == NULL) {
+ videoflinger_device_create_new_surface (videodev);
+ }
+
+ /* use double buffer in post */
+ int frameSize = videodev->width * videodev->height * 2;
+ GST_INFO
+ ("format=%d, width=%d, height=%d, hor_stride=%d, ver_stride=%d, frameSize=%d",
+ videodev->format, videodev->width, videodev->height, videodev->hor_stride,
+ videodev->ver_stride, frameSize);
+
+ /* create frame buffer heap base */
+ videodev->frame_heap = new MemoryHeapBase (frameSize * MAX_FRAME_BUFFERS);
+ if (videodev->frame_heap->heapID () < 0) {
+ GST_ERROR ("Error creating frame buffer heap!");
+ return -1;
+ }
+
+ /* create frame buffer heap and register with surfaceflinger */
+ ISurface::BufferHeap buffers (videodev->width,
+ videodev->height,
+ videodev->hor_stride,
+ videodev->ver_stride, videodev->format, videodev->frame_heap);
+
+ if (videodev->isurface->registerBuffers (buffers) < 0) {
+ GST_ERROR ("Cannot register frame buffer!");
+ videodev->frame_heap.clear ();
+ return -1;
+ }
+
+ for (int i = 0; i < MAX_FRAME_BUFFERS; i++) {
+ videodev->frame_offset[i] = i * frameSize;
+ }
+ videodev->buf_index = 0;
+ GST_INFO ("Leave");
+
+ return 0;
+}
+
+void
+videoflinger_device_unregister_framebuffers (VideoFlingerDeviceHandle handle)
+{
+ GST_INFO ("Enter");
+
+ if (handle == NULL) {
+ return;
+ }
+
+ VideoFlingerDevice *videodev = (VideoFlingerDevice *) handle;
+ if (videodev->frame_heap.get ()) {
+ GST_INFO ("Unregister frame buffers. videodev->isurface = %p",
+ videodev->isurface.get ());
+
+ /* release ISurface */
+ GST_INFO ("Unregister frame buffer");
+ videodev->isurface->unregisterBuffers ();
+
+ /* release MemoryHeapBase */
+ GST_INFO ("Clear frame buffers.");
+ videodev->frame_heap.clear ();
+
+ /* reset offset */
+ for (int i = 0; i < MAX_FRAME_BUFFERS; i++) {
+ videodev->frame_offset[i] = 0;
+ }
+
+ videodev->format = -1;
+ videodev->width = 0;
+ videodev->height = 0;
+ videodev->hor_stride = 0;
+ videodev->ver_stride = 0;
+ videodev->buf_index = 0;
+ }
+
+ GST_INFO ("Leave");
+}
+
+void
+videoflinger_device_post (VideoFlingerDeviceHandle handle, void *buf,
+ int bufsize)
+{
+ GST_INFO ("Enter");
+
+ if (handle == NULL) {
+ return;
+ }
+
+ VideoFlingerDevice *videodev = (VideoFlingerDevice *) handle;
+
+ if (++videodev->buf_index == MAX_FRAME_BUFFERS)
+ videodev->buf_index = 0;
+
+ memcpy (static_cast <
+ unsigned char *>(videodev->frame_heap->base ()) +
+ videodev->frame_offset[videodev->buf_index], buf, bufsize);
+
+ GST_INFO ("Post buffer[%d].\n", videodev->buf_index);
+ videodev->isurface->postBuffer (videodev->frame_offset[videodev->buf_index]);
+
+ GST_INFO ("Leave");
+}
diff --git a/sink/surfaceflingersink/surfaceflinger_wrap.h b/sink/surfaceflingersink/surfaceflinger_wrap.h
new file mode 100644
index 0000000..31fa2a5
--- /dev/null
+++ b/sink/surfaceflingersink/surfaceflinger_wrap.h
@@ -0,0 +1,61 @@
+/* GStreamer
+ * Copyright (C) <2009> Prajnashi S <prajnashi@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This file defines APIs to convert C++ ISurface
+ * interface to C interface
+ */
+#ifndef __SURFACE_FLINGER_WRAP_H__
+#define __SURFACE_FLINGER_WRAP_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+
+ typedef void *VideoFlingerDeviceHandle;
+
+ typedef enum
+ {
+ VIDEO_FLINGER_RGB_565 = 1,
+ VIDEO_FLINGER_RGB_888 = 2,
+ } VIDEO_FLINGER_PIXEL_FORMAT;
+
+ VideoFlingerDeviceHandle videoflinger_device_create (void *isurface);
+ int videoflinger_device_release (VideoFlingerDeviceHandle handle);
+ int videoflinger_device_register_framebuffers (VideoFlingerDeviceHandle
+ handle, int w, int h, VIDEO_FLINGER_PIXEL_FORMAT format);
+ void videoflinger_device_unregister_framebuffers (VideoFlingerDeviceHandle
+ handle);
+ void videoflinger_device_post (VideoFlingerDeviceHandle handle, void *buf,
+ int bufsize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__SURFACE_FLINGER_WRAP_H__*/
diff --git a/sink/surfaceflingersink/wrap_test.cpp b/sink/surfaceflingersink/wrap_test.cpp
new file mode 100644
index 0000000..e427907
--- /dev/null
+++ b/sink/surfaceflingersink/wrap_test.cpp
@@ -0,0 +1,71 @@
+/* GStreamer
+ * Copyright (C) <2009> Prajnashi S <prajnashi@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This is a surface flinger wrapper test application
+ */
+
+#include <stdio.h>
+#include "surfaceflinger_wrap.h"
+#include <cutils/log.h>
+#include <ui/PixelFormat.h>
+using namespace android;
+
+#define LOG_TAG "surfacewrapper_test"
+
+#define WIDTH 320
+#define HEIGHT 240
+#define MAX_STEP 200
+
+
+int
+main (int argc, char **argv)
+{
+ VideoFlingerDeviceHandle videodev = NULL;
+ short framebuf[WIDTH * HEIGHT];
+
+ LOGE ("surface_test, enter\n");
+
+ // create surface flinger
+ videodev = videoflinger_device_create (NULL);
+
+ // register buffer
+ videoflinger_device_register_framebuffers (videodev, WIDTH, HEIGHT,
+ VIDEO_FLINGER_RGB_565);
+
+ for (int step = 0; step < MAX_STEP; step++) {
+ for (int i = 0; i < WIDTH * HEIGHT; i++) {
+ framebuf[i] = (short) (i + step * 16);
+ }
+ // post buffer
+ videoflinger_device_post (videodev, framebuf,
+ WIDTH * HEIGHT * sizeof (short));
+ usleep (5 * 1000 * 10);
+ }
+
+ // unregister buffer
+ videoflinger_device_unregister_framebuffers (videodev);
+
+ // free buffer
+ videoflinger_device_release (videodev);
+
+ LOGE ("surface_test, leave\n");
+
+ return 0;
+}