Casting an Encoder to an ad insertion server

Started by Chris Oakley,

Chris Oakley

I'm trying to diagnose an issue we have with streaming to an ad insertion service.

If you're not familiar with those, you connect to the ad insertion streaming server and send specific meta data to it to tell the service to play an ad on your stream. These ads then play, replacing the one that you may be sending locally. When it works it's seemless.

However, trying to make sure it stays in sync is a problem, and I'm sure it'll be some flag somewhere on the BASS encoder.

I've spent a week on it and I've confirmed that the meta is being sent at the correct time using the BASS_Encode_CastSetTitle method, so it's not that.

From what I can tell, there are sometimes gaps in the audio on the stream. I think the user connecting to the service has a bad internet service which briefly interupts the data flow and the ingestion at the server side. I don't know anything about how the ad insertion service deals with incoming audio and meta. I set a ping going pinging google.com with the -t flag so it keeps pinging and when the audio stops on the stream I can see the ping timeout.

If we assume that the delay is buffering causing the meta to be recieved ahead of where it should be, then this would account for the ads coming in later and later.

Is there anything I can do to try and keep things in time?

This is my encoder:
_Encoder = BassEnc.BASS_Encode_Start(m_StreamCloneMixerId, _EncoderString, BASSEncode.BASS_ENCODE_NOHEAD Or BASSEncode.BASS_ENCODE_FP_16BIT Or BASSEncode.BASS_ENCODE_QUEUE, Nothing, IntPtr.Zero)
It may be that the whole system needs a reliable internet connection.

Ian @ un4seen

If the problem is happening when the internet connection is bad, then that seems like it's the cause of the problem. If the server has a buffer size config option, you could try increasing that to ride out the connection dropouts, but that will increase latency.

Regarding keeping metadata and audio in sync, what type of server is it and what audio format are you sending it? If it's Icecast-compatible then you can properly synchronise metadata and audio by using an Ogg-based format and starting a new bitstream for each metadata update, eg. use BASS_Encode_OGG_NewStream instead of BASS_Encode_CastSetTitle. An example of doing this can be found in the CAST.C example in the BASSenc package. Note you need to be using a BASSenc encoder (not external EXE) for this, eg. BASS_Encode_OGG_Start instead of BASS_Encode_Start.

Chris Oakley

Thanks Ian. It's an IceCast server and I'm sending MP3, Stereo.

So in this case should I use BASS_Encode_OGG_NewStream? If so, what is the difference between that and BASS_Encode_CastSetTitle and should I be concerned about this in the documentation:

QuoteStarting a new bitstream involves writing new headers, which can be quite large (usually around 4KB), so new bitstreams should not be used too frequently to avoid excessive overhead.

Ian @ un4seen

The big difference is that BASS_Encode_CastSetTitle uses the Shoutcast metadata system, while the "NewStream" method doesn't. Does your ad insertion service require Shoutcast metadata? If not, you could give the "NewStream" method a try and see if it works better for you.

Chris Oakley

That's a good question about what they require. Sadly support and information is sketchy so I don't know about needing ShoutCast meta.

I'll certainly give it a try and feed back what happens. If it works can I just leave this in place for all IceCast streams or do I need to make it for a specific circumstance?

Thanks again.

Chris Oakley

I tried:
_result = AddOn.EncOgg.BassEnc_Ogg.BASS_Encode_OGG_NewStream(_Encoder, $"-t ""{_Meta}""", 0)
but sadly it returns false so either I've got something wrong or the stream just doesn't like it.

Ian @ un4seen

Did you also replace your BASS_Encode_Start call with BASS_Encode_OGG_Start? Please note that BASS_Encode_OGG_NewStream only applies to encoders started with BASS_Encode_OGG_Start.

You could also try testing with the pre-compiled CAST-ADDONS.EXE example included in the BASSenc package (C\BIN folder), ie. try connecting to your server with that and see if metadata updates work. Note you will also need the BASSenc_MP3/OGG/OPUS/FLAC add-ons to run that.

Chris Oakley

I didn't start it with BASS_Encode_OGG_Start as I'm using the regular BASS_Encode_Start but with a command line for OGG. I suspect you'll tell me that's no good?

My concern here is that this means that this will only work as an OGG stream?

I'll see if I can locate the CAST-ADDONS.EXE.

Ian @ un4seen

Yes, I'm afraid BASS_Encode_Start (eg. with oggenc.exe) won't work with BASS_Encode_OGG_NewStream. It needs to be BASS_Encode_OGG_Start. Opus and FLAC encoding options are also available with BASSenc_OPUS and BASSenc_FLAC. If you want MP3 encoding then you'll have to stick with BASS_Encode_CastSetTitle, as this bitstream metadata method isn't possible with MP3.

Chris Oakley

I think the issue is coming from a bad internet connection with the client. I've been running a constant ping test to Google.com and logging it out to a file and when there is no-reply logged it seems to throw the stream out / delay the stream.

This makes sense, as there is a buffer at the ad insertion service so you wouldn't notice anything audibly when listening, but if there is a bad connection then the encoder must be backing up, which tallies with what I'm then hearing as the meta is triggering ads ahead of where they should be.

I have tried lowering the queue using:
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_ENCODE_QUEUE, 250)
but BASS keeps throwing back the BASS_ERROR_ILLPARAM error.

I can't even get the current value using
Bass.BASS_GetConfig(BASSConfig.BASS_CONFIG_ENCODE_QUEUE)
as again I get the same error.

I know I must be missing something obvious here. Any thoughts?

Ian @ un4seen

I suspect BASSENC.DLL hasn't been loaded yet at the time of those calls. You can force it to be loaded by calling a function from it, eg. adding a BASS_Encode_GetVersion call to your initialization code.

Chris Oakley

Ahhh - a simple "Gotcha". Thanks Ian. So I've checked this is now implementing this value, so I guess I just need to see what happens.