[gst-devel] directsound sink

John Janecek nopa90 at gmail.com
Fri Dec 23 23:33:05 CET 2005


Here is a direct sound sink i make for gstreamer0.10

I make my plugs with gob2.
Cool part of my sink it does not require linking against MS dll since
it loads the dsounddll dynamically.


----------------------------Sconstruct-------------------------------------------------------------
import sys
import string
env = Environment()

def parse_conf(env, output):
    dict = {
        'ASFLAGS'       : [],
        'CCFLAGS'       : [],
        'CPPFLAGS'      : [],
        'CPPPATH'       : [],
        'LIBPATH'       : [],
        'LIBS'          : [],
        'LINKFLAGS'     : [],
    }
    static_libs = []

    params = string.split(output)
    for arg in params:
        arg = arg.replace("/usr/local","C:/root/local")
        if arg[0] != '-':
            static_libs.append(arg)
        elif arg[:2] == '-L':
            dict['LIBPATH'].append(arg[2:])
        elif arg[:2] == '-l':
            dict['LIBS'].append(arg[2:])
        elif arg[:2] == '-I':
            dict['CPPPATH'].append(arg[2:])
        elif arg[:4] == '-Wa,':
            dict['ASFLAGS'].append(arg)
        elif arg[:4] == '-Wl,':
            dict['LINKFLAGS'].append(arg)
        elif arg[:4] == '-Wp,':
            dict['CPPFLAGS'].append(arg)
        elif arg == '-pthread':
            dict['CCFLAGS'].append(arg)
            dict['LINKFLAGS'].append(arg)
        else:
            dict['CCFLAGS'].append(arg)
    apply(env.Append, (), dict)
    return static_libs

env.ParseConfig('pkg-config --cflags --libs gstreamer-0.10',parse_conf)
if sys.platform == "win32" :
	env.Command("ds-sink.cc",'ds_sink.gob',"gob2 --for-cpp $SOURCE")
	env.Append(CPPPATH=["E:/msys/1.0/local/include","E:/GTK/include/glib-2.0","../../../DirXSDK/Include"])
	env.Append(LIBS=['gstaudio-0.10','gstbase-0.10','gstreamer-0.10',"gobject-2.0",'glib-2.0'])
	#env.Append(CCFLAGS = '-g')
sink = env.SharedLibrary(target ="direct-sound",
		source = ["ds-sink.cc"],
		SHLIBPREFIX='')
if sys.platform == "win32" :
	env.Install("E:/gplugs",sink)
	env.Alias('install','E:/gplugs')

---------------------------ds_header.h-----------------------------------------------------------
typedef HRESULT (WINAPI *FNDSCREATE)(LPCGUID pcGuidDevice,
LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
#define PRIV(x) self->_priv->x
#define _PRIV self->_priv
----------------------------ds_sink.gob-----------------------------------------------------------
%alltop{
#include <gst/gst.h>
#include <gst/audio/gstaudiosink.h>
#include <windows.h>
#include <dsound.h>
#include <mmsystem.h>
#include "ds_header.h"
//#include <stdio.h>
%}

%{
//gdb "gst-launch-0.10 audiotestsrc ! audioconvert ! dssink"
#include <stdio.h>
#define VERSION "0.01"
#define PACKAGE "dssink"
#define D_OUT(x) g_warning(x)
//#define BREAKPOINT __asm__("int3");
#define BREAKPOINT "";

static GstStaticPadTemplate dssink_sink_template_factory =
	GST_STATIC_PAD_TEMPLATE ("sink",
	GST_PAD_SINK,
	GST_PAD_ALWAYS,
	GST_STATIC_CAPS ("audio/x-raw-int, "
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
		"endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, "
#else
		"endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
#endif
		"signed = (boolean) TRUE, "
		"width = (int) 16, "
		"depth = (int) 16, "
		"rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
		"audio/x-raw-int, "
		"signed = (boolean) { TRUE, FALSE }, "
		"width = (int) 8, "
		"depth = (int) 8, "
		"rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
	);
	
%}

class Ds:Sink from Gst:Audio:Sink {
	protected HMODULE hLibDSound;
	protected FNDSCREATE DirectSoundCreate8;
	protected LPDIRECTSOUND lpDS;
	protected DSBUFFERDESC dsBufferDesc;
	protected LPDIRECTSOUNDBUFFER lpDSB1st;
	protected LPDIRECTSOUNDBUFFER lpDSB2nd;
	protected WAVEFORMATEX wfxWaveFormat;
	protected char *device_name = {g_strdup("None")}
				destroywith g_free;
	protected gboolean playing;
	protected gint bytes_per_sample;
	
	protected gboolean first_write;
	protected DWORD write_pos;
	protected gint buffer_size; //size of the buffer in bytes
	
	property STRING device_name
		(nick = "DeviceName",
		blurb = "Name of Device",
		default_value = "None",
		/* Export get/set functions for this property */
		export,
		link);
		
	init(self) {
		D_OUT("dsink init");
		self->hLibDSound = NULL;
		self->DirectSoundCreate8 = NULL;
		self->playing = FALSE;
		if(!(self->hLibDSound = (HMODULE) LoadLibrary("dsound.dll"))) {
			D_OUT("dsound.dll failed to load");
			}
		if(!(self->DirectSoundCreate8 =
(FNDSCREATE)GetProcAddress(self->hLibDSound,"DirectSoundCreate8"))) {
			D_OUT("Unable to get ProcAddess of DirectSoundCreate8");
			}
		}
		
	override (G:Object) void
	finalize(GObject* obj) {
		D_OUT("dsink finalize");
		Self *self = SELF (obj);
		if (self->lpDS) IDirectSound_Release(self->lpDS);
		if (self->hLibDSound) FreeLibrary(self->hLibDSound);
		PARENT_HANDLER(obj);
		}
		
	override (Gst:Element) GstStateChangeReturn
	change_state(GstElement *element, GstStateChange transition) {
		GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
		Self *self = SELF (element);
		ret = PARENT_HANDLER(element,transition);
		
		switch(transition) {
			case GST_STATE_CHANGE_NULL_TO_READY :
				D_OUT("dsink GST_STATE_CHANGE_NULL_TO_READY");
				break;
			case GST_STATE_CHANGE_READY_TO_PAUSED :	
				D_OUT("dsink GST_STATE_CHANGE_READY_TO_PAUSED");
				break;
			case GST_STATE_CHANGE_PAUSED_TO_PLAYING :
				D_OUT("dsink GST_STATE_CHANGE_PAUSED_TO_PLAYING");
				if (DS_OK != IDirectSoundBuffer_Play(self->lpDSB2nd,0,0,DSBPLAY_LOOPING)) {
					g_warning("failed to set sound buffer to play");
					}	
				break;
			case GST_STATE_CHANGE_PLAYING_TO_PAUSED :
				D_OUT("dsink GST_STATE_CHANGE_PLAYING_TO_PAUSED");
				if (self->playing) IDirectSoundBuffer_Stop(self->lpDSB2nd);
				break;
			case GST_STATE_CHANGE_PAUSED_TO_READY :
				D_OUT("dsink GGST_STATE_CHANGE_PAUSED_TO_READY");
				break;
			case GST_STATE_CHANGE_READY_TO_NULL :
				D_OUT("dsink GST_STATE_CHANGE_READY_TO_NULL");
				break;
			default :
				break;
			}
		
		return ret;
		}
	
	override (Gst:BaseSink) GstCaps*
	get_caps (GstBaseSink *sink) {
		D_OUT("dsink get_caps");
		/*
		GstStructure *structure;
		GstCaps *caps;
		gint w,h;
		Self *self = SELF (sink);
		//g_warning ("buffersink get_caps");
		caps = gst_caps_copy(gst_pad_get_pad_template_caps (sink->sinkpad));
		return caps;
		*/
		return NULL;
		}
		
	
	
	override (Gst:Audio:Sink) gboolean
	open(GstAudioSink *sink) {
		BREAKPOINT
		D_OUT("dsink open");
		Self *self=SELF(sink);
		
		if (FAILED(self->DirectSoundCreate8(NULL,&(self->lpDS),NULL))) {
			return FALSE;
			}
		//GetDesktopWindow()
		if (DS_OK != IDirectSound_SetCooperativeLevel(self->lpDS,GetDesktopWindow(),DSSCL_PRIORITY))
{
			return FALSE;
			}
		//Creating Primary Buffer
		ZeroMemory(&(self->dsBufferDesc), sizeof(self->dsBufferDesc));
		self->dsBufferDesc.dwSize = sizeof(self->dsBufferDesc);
		self->dsBufferDesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
		if (DS_OK != IDirectSound_CreateSoundBuffer(self->lpDS,&self->dsBufferDesc,&self->lpDSB1st,NULL))
{
			return FALSE;
			}
		return TRUE;
		}
		
	override (Gst:Audio:Sink) gboolean
	prepare(GstAudioSink *sink, GstRingBufferSpec *spec) {
		D_OUT("dsink prepare");
		BREAKPOINT
		Self *self=SELF(sink);
		self->playing = FALSE;
		self->bytes_per_sample = spec->bytes_per_sample;
		//Creating Secondary Buffer
		//g_warning("dssink prepare width %d,depth %d,rate %d,channels %d,
bytes per sample %d",
		//					spec->width,spec->depth,spec->rate,spec->channels,spec->bytes_per_sample);
		self->wfxWaveFormat.wFormatTag = WAVE_FORMAT_PCM;
		self->wfxWaveFormat.nChannels = spec->channels;
		self->wfxWaveFormat.nSamplesPerSec = spec->rate;
		self->wfxWaveFormat.nAvgBytesPerSec = spec->bytes_per_sample*spec->rate;
		self->wfxWaveFormat.nBlockAlign = (spec->channels*spec->width)/8;
		self->wfxWaveFormat.wBitsPerSample = spec->width;
		self->wfxWaveFormat.cbSize = 0;
		
		self->buffer_size = spec->bytes_per_sample*spec->rate;
		
		self->dsBufferDesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 |
DSBCAPS_GLOBALFOCUS;	// 0x1c000
		self->dsBufferDesc.dwBufferBytes = self->buffer_size;
		self->dsBufferDesc.lpwfxFormat = &self->wfxWaveFormat;
		
		HRESULT res = IDirectSound_CreateSoundBuffer(self->lpDS,
&self->dsBufferDesc, &(self->lpDSB2nd), NULL);
		if (res!=DS_OK) {
			g_warning("failed to create secondary sound buffer %x",res);
			return FALSE;
			}
			
		LPDWORD lp1;
		DWORD l1;
		IDirectSoundBuffer_Lock(self->lpDSB2nd, 0, 0, (LPVOID *)(&lp1), &l1,
NULL, NULL,DSBLOCK_ENTIREBUFFER);
		ZeroMemory(lp1,l1); //not correct for signed samples
		IDirectSoundBuffer_Unlock(self->lpDSB2nd,(LPVOID)(lp1),l1,NULL,NULL);
		
		self->first_write = TRUE;
		self->playing = TRUE;
		return TRUE;
		}
		
	override (Gst:Audio:Sink) gboolean
	unprepare(GstAudioSink *sink) {
		D_OUT("dsink unprepare");
		Self *self=SELF(sink);
		if (self->lpDSB2nd) IDirectSoundBuffer_Release(self->lpDSB2nd);
		self->playing = FALSE;
		return TRUE;
		}
		
	override (Gst:Audio:Sink) gboolean
	close(GstAudioSink *sink) {
		D_OUT("dsink close");
		Self *self=SELF(sink);
		if (self->lpDSB1st) IDirectSoundBuffer_Release(self->lpDSB1st);
		return TRUE;
		}
		
	override (Gst:Audio:Sink) guint
	write(GstAudioSink *sink, gpointer data, guint length) {
		//length is the length of the buffer in bytes
		BREAKPOINT
		Self *self=SELF(sink);
		//g_warning("dsink write %d",length);
		
		DWORD PlayOffset,WriteOffset;
		IDirectSoundBuffer_GetCurrentPosition(self->lpDSB2nd,&PlayOffset,&WriteOffset);
		
		if(self->first_write) {
			self->first_write=FALSE;
			self->write_pos = WriteOffset;
			}
		
		
		DWORD delta=0;
		if(self->write_pos<PlayOffset) {
			delta = (self->buffer_size - PlayOffset) + self->write_pos;
			}
		else {
			delta = self->write_pos - PlayOffset;
			}
		if(delta>=(self->buffer_size/2)) return 0;
		
		LPVOID ptr1,ptr2;
		DWORD len1,len2;
		HRESULT R=IDirectSoundBuffer_Lock(self->lpDSB2nd,self->write_pos,length,
					(LPVOID *)(&ptr1),&len1,(LPVOID *)(&ptr2),&len2,0);
		if(R!=DS_OK) {
			D_OUT("unable to lock buffer");
			return 0;
			}
		memcpy(ptr1,data,len1);
		if(len1<length) {
			memcpy(ptr2,(void*)(((char *)data)+len1),len2);
			}
		IDirectSoundBuffer_Unlock(self->lpDSB2nd,(LPVOID)ptr1,len1,(LPVOID)(ptr2),len2);
		self->write_pos+=length;
		if(self->write_pos>=self->buffer_size) {
			self->write_pos-=self->buffer_size;
			}
		return length;
		}
		
	override (Gst:Audio:Sink) guint
	delay(GstAudioSink *sink) {
		Self *self=SELF(sink);
		if(!self->lpDSB2nd) {
			return 0;
			}
		//D_OUT("ds_sink delay");
		DWORD PlayOffset,WriteOffset;
		IDirectSoundBuffer_GetCurrentPosition(self->lpDSB2nd,&PlayOffset,&WriteOffset);
		//g_warning("dsink delay %d %d",WriteOffset,PlayOffset);
		//g_warning("dsink delay %d",(WriteOffset -
PlayOffset)/self->bytes_per_sample);
		guint delay = 0;
		if(!self->first_write) {
			WriteOffset=self->write_pos;
			}
		if(WriteOffset>=PlayOffset) {
			delay = (WriteOffset - PlayOffset)/self->bytes_per_sample;
			}
		else {
			delay = (self->buffer_size - (PlayOffset -
WriteOffset))/self->bytes_per_sample;
			}
		//g_warning("dsink delay %d",delay);
		return delay;
		}
	
	override (Gst:Audio:Sink) void
	reset(GstAudioSink *sink) {
		D_OUT("ds_sink reset");
		}
	
	public void
	base_init (gpointer g_class) {
		D_OUT("ds_sink base_init");
		static GstElementDetails sink_details =
		GST_ELEMENT_DETAILS (
		"DsSink",
		"Sink/Audio",
		"DirectSound sink",
		"John Janecek nopa90 at gmail.com");
		
		GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
		
		gst_element_class_add_pad_template (element_class,
				gst_static_pad_template_get (&dssink_sink_template_factory));
		
		gst_element_class_set_details (element_class, &sink_details);
		}

	public GType
	get_type (void) {
		static GType type = 0;
		//D_OUT("ds_sink get_type");
		if(!type) {
			static const GTypeInfo info = {
				sizeof (SelfClass),
				(GBaseInitFunc) ds_sink_base_init,
				(GBaseFinalizeFunc) NULL,
				(GClassInitFunc) ds_sink_class_init,
				(GClassFinalizeFunc) NULL,
				NULL /* class_data */,
				sizeof (Self),
				0 /* n_preallocs */,
				(GInstanceInitFunc)ds_sink_init,
				NULL
				};
			//type registered should be parent type
			type = g_type_register_static
(GST_TYPE_AUDIO_SINK,"DsSink",&info,(GTypeFlags)0);
			}
		return type;
		}
}

%{
static gboolean
plugin_init (GstPlugin * plugin)
	{
	//BREAKPOINT
	D_OUT("ds_sink plugin_init");
	return gst_element_register (plugin,"dssink", //name here has to match
		GST_RANK_NONE,
		DS_TYPE_SINK);
	}

GST_PLUGIN_DEFINE (
	GST_VERSION_MAJOR,
	GST_VERSION_MINOR,
	"dssink",
	"dssink plugin",
	plugin_init,
	VERSION,
	"LGPL",
	"GStreamer",
	"http://gstreamer.net/"
	)
%}




More information about the gstreamer-devel mailing list