22 May '13 - 23:01 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]
  Reply  |  Print  
Author Topic: Virtual Playback Device ( Shoutcast streams)  (Read 847 times)
Smoov
Posts: 12


« on: 7 Feb '12 - 21:28 »
Reply with quoteQuote

The architecture of our player uses a NON stop mixer that is hooked to a device for playback.  When then attach files to the mixer as we wish them to play.. This is basically how we handle overlapping and fading of audio…

I’m trying to do the exact same thing with a shoutcast stream ( creating a virtual Playback device). To do this I use the NO Sound device and a decode mixer…

My problem is when I set up a thread to pull data through a mixed I pull the data to fast? How do I go about pulling data at the proper playback rate?  SO I’m feeding my encoders and dsp as if I were playing the audio to a normal playback device?

I try and set up a thread to pull data through the mixer like so:

//Used as a null render thread data pump..
void NullStreamThreadProc( void *param )
{
char buffer[20000];
DWORD rtnlength = 0;
RcsDevice* pRender = (RcsDevice*)param;
if(pRender)
{
pRender->_bNullRender = true;
DWORD hMixer  = pRender->_dwMixerHandle;

while ( hMixer != 0  && BASS_ChannelIsActive(hMixer) )
{
//IM ASSUMING I SHOULD PULL DATA HERE AT SOME CONSTANT RATE?

rtnlength = BASS_ChannelGetData(hMixer, buffer ,20000);

//There is no data in the mixer, wait for some..
if(buffer[0] == 0 )
Sleep(10);
}

//close render stream..
pRender->_bNullRender = false;
pRender->_hNullThread  = 0;
}
else
{
assert(!"*** ERROR *** BASS - NullStreamThreadProc CALL - RCSDevice is NULL!");
}
_endthread();
}

Below is the core function used to create the virtual mixer device..

HSTREAM  CRCSBassPlayer::VerifyVirtualOutputMixer( RcsDevice* pRCSDevice )
{
BOOL bStarted = FALSE;
HSTREAM mixerstream  = 0;
if( pRCSDevice )
{
mixerstream = pRCSDevice->_dwMixerHandle;

//do we need to create one..
if(  mixerstream == 0 && IS_RCSVIRTUALID(pRCSDevice->_DeviceId) )
{
BASS_SetDevice( 0 );
CCaster* pCaster = &(pRCSDevice->_VirtualOutputDeviceInfo.Caster);

if( pCaster )
{
DWORD dwFrequency = pCaster->EncoderSamplerate;
DWORD dwChannels = 2; //pCaster->EncoderChannels;
DWORD dwBitRate = pCaster->EncoderBitrate;

//Create a new mixer for the device (software mixing)
mixerstream = BASS_Mixer_StreamCreate(dwFrequency, dwChannels, BASS_SAMPLE_FLOAT| BASS_STREAM_DECODE | BASS_MIXER_NONSTOP);

//Create command line decoder...
CAtlString szEncoder= _T("lame -r -s 44100 -b 128 -");
LPCSTR encodetype =  BASS_ENCODE_TYPE_MP3;

if( 0 == pCaster->xEncoderFormat.CollateNoCase( _T(CASTERFORMAT_MP3) ) )
{
szEncoder.Format( _T("lame -r -s %d -b %d -") ,dwFrequency , dwBitRate);
encodetype =  BASS_ENCODE_TYPE_MP3;
}


HENCODE hEncoder = BASS_Encode_Start(mixerstream, CT2A(szEncoder), BASS_ENCODE_NOHEAD|BASS_ENCODE_AUTOFREE, NULL, 0);
if( hEncoder )
{
bool bServer =  (0 == pCaster->xCasterType.CollateNoCase( _T(CASTERTYPE_SHOUTSERVER) ));
if( !bServer )
{

BASS_Encode_CastInit(hEncoder, CT2A(pCaster->CasterAddress), "", encodetype, CT2A(pCaster->PublishName), CT2A(pCaster->PublishURL)
, CT2A(pCaster->PublishGenre), CT2A(pCaster->PublishDesc), NULL, dwBitRate, pCaster->PublishPublic);
}
else
DWORD buffer =  (DWORD)((double)dwBitRate * 125.0 *  pCaster->CasterBuffer); //5 second buffer
bStarted = BASS_Encode_ServerInit (hEncoder, CT2A(pCaster->CasterAddress), buffer, buffer, 0, NULL, NULL); // start the server
}
}
else
{
int errorcode = BASS_ErrorGetCode();
CAtlString msg;
msg.Format(_T("Problem Creating Virtual: Output Device:#%d [`%s`] - Mixer:%d , Error Code: %d CommandLine: %s" )
, pRCSDevice->_DeviceId, pRCSDevice->_DeviceKey, mixerstream, errorcode, szEncoder);
m_Logger.LogIt(_T(__FUNCTION__), xLoggerLib::TCL_ERROR, msg.GetBuffer());
}

if( bStarted && mixerstream != 0)
{
//SPECIAL CASE: START THE NULL PLAYDEVICE THREAD!
{
//Don't start the thread until the mixer handle is assigned
pRCSDevice->_hNullThread = (HANDLE) _beginthread(NullStreamThreadProc, 0, (void*)pRCSDevice);
}

//Set up callback for encoder notifies...
BASS_Encode_SetNotify(hEncoder,EncoderNotify,0);

pRCSDevice->_dwMixerHandle = mixerstream;
CAtlString msg;
msg.Format(_T("Created: Output Device: %d, Mixer:%d [`%s`]"), pRCSDevice->_DeviceId, mixerstream, pRCSDevice->_DeviceKey);
m_Logger.LogIt(_T(__FUNCTION__), xLoggerLib::TCL_STATS, msg.GetBuffer());
}
else
{
int errorcode = BASS_ErrorGetCode();
CAtlString msg;
msg.Format(_T("Problem Creating Virtual: Output Device:#%d [`%s`] - Mixer:%d , Error Code: %d"), pRCSDevice->_DeviceId, pRCSDevice->_DeviceKey, mixerstream, errorcode);
m_Logger.LogIt(_T(__FUNCTION__), xLoggerLib::TCL_ERROR, msg.GetBuffer());

pRCSDevice->releaseHandles();
pRCSDevice->reset();
mixerstream = 0;
}
}
}
}

return mixerstream;
}
Logged
Smoov
Posts: 12


« Reply #1 on: 8 Feb '12 - 13:11 »
Reply with quoteQuote

I have found one solution, although I feel like i'm miss-using the system.

My solution right now is to put the mixer on the default output.  And push the volume on the mixer to Silence since there is no physical playback device needed. One advantage I could see is the solution lets me preview what is going to shoutcast, Although current this feature is not needed.

BOOL bInit = BASS_Init( -1, 44100, 0 , 0,  NULL);
BOOL MixerStarted  = FALSE;
// mixerstream = BASS_Mixer_StreamCreate(dwFrequency, dwChannels, BASS_SAMPLE_FLOAT | BASS_MIXER_NONSTOP);
mixerstream = BASS_Mixer_StreamCreate( 44100, 2, BASS_MIXER_NONSTOP);
if(mixerstream != 0)
{
BASS_ChannelSetAttribute(mixerstream, BASS_ATTRIB_VOL, 0 ); // mute the mixer
MixerStarted  = BASS_ChannelPlay(mixerstream, FALSE); // start the mixer
}

My concern is that output is being used for other playback in our software, the solution does provides proper playback timing.

Is the solution unnecessary and does it cause additional processing on the system?
Logged
Ian @ un4seen
Administrator
Posts: 15269


« Reply #2 on: 8 Feb '12 - 17:08 »
Reply with quoteQuote

Unless the BASS_ENCODE_CAST_NOLIMIT flag is used, BASSenc should automatically limit the flow of data to real-time speed when feeding a Shoutcast/Icecast server. So you shouldn't need to do anything special yourself in that regard, ie. you can simply call BASS_ChannelGetData in a tight loop...

while (BASS_ChannelIsActive(hMixer) && BASS_Encode_IsActive(hMixer)) {
BYTE buffer[20000];
BASS_ChannelGetData(hMixer, buffer, 20000);
}

It looks like that is pretty much what you were doing in your NullStreamThreadProc function. Was that not working well?
Logged
Smoov
Posts: 12


« Reply #3 on: 8 Feb '12 - 17:51 »
Reply with quoteQuote

I have ran all my test using BASS_Encode_ServerInit, but if I use no Playback device, the files i plug-into the mixer are processed as fast as they can. Just as if i was analyzing the file for waveform or something...


I have not ran any test using BASS_Encode_CastInit maybe this is the difference?

But under my current test without an actual playback device using a no-sound playback device, with a decode thread using a tight loop, data is processed way to fast...

I will try and run some castInit test, to see if this would be different.
Logged
Ian @ un4seen
Administrator
Posts: 15269


« Reply #4 on: 8 Feb '12 - 18:06 »
Reply with quoteQuote

Ah, right. The rate limiting isn't enabled by default with the built-in server; the BASS_ENCODE_LIMIT flag needs to be used to enable it in that case. The reason for that asymmetry is that the rate limiting was automatic when the casting feature was introduced, but it became optional by the time that the built-in server feature arrived.
Logged
Smoov
Posts: 12


« Reply #5 on: 8 Feb '12 - 18:29 »
Reply with quoteQuote

BASS_ENCODE_LIMIT

that did the trick, i can use my decode mixer...

hEncoder = BASS_Encode_Start(mixerstream, CT2A(szEncoder), BASS_ENCODE_LIMIT|BASS_ENCODE_NOHEAD|BASS_ENCODE_AUTOFREE, NULL, 0);
Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines