diff options
author | Thibault Saunier <thibault.saunier@collabora.co.uk> | 2011-04-12 13:05:31 -0400 |
---|---|---|
committer | Reynaldo H. Verdejo Pinochet <reynaldo.verdejo@collabora.co.uk> | 2011-04-22 12:27:52 -0400 |
commit | 878fe2d7bc74820e6de34cb28cbc2cfb638ec478 (patch) | |
tree | afdfa9092c168cfa6f238ec37edf5da282d2d7d8 | |
parent | bead0f31f9973487a351d9f1143e7b53210d0c89 (diff) |
Add audioflingersink and surfaceflingersink
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | sink/audioflingersink/Android.mk | 63 | ||||
-rw-r--r-- | sink/audioflingersink/GstAndroid.cpp | 37 | ||||
-rw-r--r-- | sink/audioflingersink/audioflinger_wrapper.cpp | 470 | ||||
-rw-r--r-- | sink/audioflingersink/audioflinger_wrapper.h | 86 | ||||
-rw-r--r-- | sink/audioflingersink/gstaudioflingerringbuffer.h | 90 | ||||
-rwxr-xr-x | sink/audioflingersink/gstaudioflingersink.c | 1585 | ||||
-rw-r--r-- | sink/audioflingersink/gstaudioflingersink.h | 70 | ||||
-rw-r--r-- | sink/surfaceflingersink/Android.mk | 40 | ||||
-rw-r--r-- | sink/surfaceflingersink/gstsurfaceflingersink.c | 386 | ||||
-rw-r--r-- | sink/surfaceflingersink/gstsurfaceflingersink.h | 60 | ||||
-rw-r--r-- | sink/surfaceflingersink/surfaceflinger_wrap.cpp | 330 | ||||
-rw-r--r-- | sink/surfaceflingersink/surfaceflinger_wrap.h | 61 | ||||
-rw-r--r-- | sink/surfaceflingersink/wrap_test.cpp | 71 |
14 files changed, 3351 insertions, 0 deletions
@@ -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; +} |