Struggling streaming to Shoutcast

Started by GTHvidsten,

GTHvidsten

I'm struggling with streaming to Shoutcast. I've set up a Shoutcast (2.6.1.777) instance and can connect to it just fine, but after a few seconds I see this error in the Shoutcast log:

QuoteShoutcast 2 source disconnected. Unable to sync to the stream. Please check the source is valid and in a supported format.

I'm streaming a 64kbps MP3 stream, which should be supported. Shoutcast even recognizes it when I start streaming by displaying this in the log:

Quotestream detected MPEG v1 layer 3 stereo

But even though it is recognized it still disconnects.

This is my sc_serv.conf:

ratelimit=0
adminpassword=hackme1
password=hackme2
requirestreamconfigs=1
streamid_1=1
streampath_1=/stream/1/

and I'm sending the following to the "server" parameter of BassEnc.BASS_Encode_CastInit:

localhost:8000,1

I'm using EncoderCMDLN with ffmpeg to encode. This works just fine with Icecast (i.e. localhost:9000/stream) with both MP3, AAC, and multiple bitrates, so I would expect this to work with Shoutcast as well, but it doesn't.
It is definitely something with this way of streaming, because using EncoderBassEnc_Mp3 works fine.
This is my EncoderCMDLN settings:

EncoderCMDLN encoder = new(_mixer)
{
    EncoderDirectory = "C:\\Path\\To\\ffmpeg",
    CMDLN_Executable = "ffmpeg.exe",
    CMDLN_CBRString = "-f s16le -ar 44100 -ac 2 -i ${input} -c:a mp3 -b:a ${kbps}k -vn -f mp3 ${output}",
    CMDLN_EncoderType = BASSChannelType.BASS_CTYPE_STREAM_MP3,
    CMDLN_DefaultOutputExtension =".mp3",
    CMDLN_Bitrate = 64,
    CMDLN_SupportsSTDOUT = true,
    CMDLN_ParamSTDIN = "-",
    CMDLN_ParamSTDOUT = "-"
};

Why can ffmpeg encode something that Icecast recognizes, but Shoutcast won't?
Is there anything I can do to make Shoutcast accept the ffmpeg mp3, while still having Icecast accept the same format?

OS: Win x64
BASS: 2.4.17.0
BASSenc: 2.4.16.1
BASSmix: 2.4.12.0
BASS.NET: 2.4.17.6

Ian @ un4seen

I'm not sure whether this is what's causing the problem, but you should probably set CMDLN_UseNOHEAD=true to disable sending a WAVE header to the encoder (to tell the sample format), as you appear to be passing the sample format via the command-line.

GTHvidsten

Thanks for the suggestion, but this unfortunately had no effect :(

Ian @ un4seen

I'm also not sure whether this will help, but you could try building the encoder command-line yourself and using that with the BASS_Encode_Start function directly, instead of using the EncoderCMDLN class (which uses BASS_Encode_Start internally).

GTHvidsten

Do you have any examples on where to start with that?

Ian @ un4seen

Based on the settings in the first post, something like this:

int encoder = BassEnc.BASS_Encode_Start(_mixer, "C:\\Path\\To\\ffmpeg\\ffmpeg.exe -f s16le -ar 44100 -ac 2 -i - -c:a mp3 -b:a 64k -vn -f mp3 -", BASSEncode.BASS_ENCODE_NOHEAD, null, IntPtr.Zero);

Please see the BASS_Encode_Start documentation for more info on it.

GTHvidsten

I did eventually understand what you were going for. Initially I thought I had to create my own EncoderCMDLN class, but when I started reading about BASS_Encode_Start() I understood what you meant :)

Unfortunately using BASS_Encode_Start() did not change anything. I can still stream just fine to Icecast, but whenever I try to cast to Shoutcast it fails with the same error as before in the Shoutcast log. A couple of times Shoutcast also gave this error:

Quotestream detected AACv4, LC (2), 44100hz, estimated 768 kbps

which is a bit weird because I never set the encoder up to use AAC @768, always MP3 @64

GTHvidsten

Additional info:

I noticed that BASS_Cast_Init() has a bitrate parameter, which I've set to 0 (zero), so I tried setting it to the correct bitrate. Unfortunately this also had no effect, but it might help with troubleshooting.

Ian @ un4seen

I have an idea what the problem is now. When casting/serving, to ensure none of its output is missed, the encoder should be created in a paused state until after the casting/server has been setup. Try adding the BASS_ENCODE_PAUSE flag to the BASS_Encode_Start call and then add a BASS_Encode_SetPaused(paused=false) call after your BASS_Encode_CastInit call.

GTHvidsten

I'm afraid that this didn't help either, but I got a slightly different log. Now I first got this:

Quote[XML] not well-formed (invalid token) at line 1

Then I get the same as before:

Quote[SRC 172.18.0.1:56052 sid=1] stream detected MPEG v1 layer 3 stereo
[SRC 172.18.0.1:56052 sid=1] stream detected MPEG v1 layer 3 stereo
[SRC 172.18.0.1:56052 sid=1] stream detected MPEG v1 layer 3 stereo
[SRC 172.18.0.1:56052 sid=1] stream detected MPEG v1 layer 3 stereo
[SRC 172.18.0.1:56052 sid=1] Shoutcast 2 source disconnected. Unable to sync to the stream. Please check the source is valid and in a supported format.


This is the essence of my code as it stands now:

string ffmpegCmd = Path.Join(ffmpegPath, ffmpegExe) + " -f s16le -ar 44100 -ac 2 -i - -c:a mp3 -b:a 64k -vn -f mp3 -";

_encoderHandle = BassEnc.BASS_Encode_Start(
    _mixer,
    ffmpegCmd,
    BASSEncode.BASS_ENCODE_NOHEAD | BASSEncode.BASS_ENCODE_PAUSE,
    null,
    IntPtr.Zero
);

BassEnc.BASS_Encode_CastInit(
    _encoderHandle,
    "localhost:8000,1",
    "mypass",
    BassEnc.BASS_ENCODE_TYPE_MP3,
    "myName",
    "myUrl",
    "myGenre",
    "myDescription",
    null,
    64,
    BASSEncodeCast.BASS_ENCODE_CAST_DEFAULT
);

BassEnc.BASS_Encode_SetPaused(_encoderHandle, false)

Ian @ un4seen

To see if the problem is something specific to ffmpeg, are things working properly if you use lame.exe instead of ffmpeg.exe?

You could also try writing the encoded data to a file via an ENCODEPROC callback function provided in the BASS_Encode_Start call, and then check if the written file is a 100% valid MP3 file.

radio42

If I recall t correctly, there was some issue with ffmpeg.exe, so I'd suggest to use lame.exe instead.

GTHvidsten

I'm struggling finding a lame.exe, but I did find one that was built for 3.100 (latests lame version), but I can't get it to work.

string encCmd = Path.Join(lamePath, "lame.exe") + " -b64 - -";
_encoderHandle = BassEnc.BASS_Encode_Start(
    _mixer,
    encCmd,
    BASSEncode.BASS_ENCODE_NOHEAD,
    null,
    IntPtr.Zero
);

From what I've been able to find of documentation, this command should encode to 64kbps from stdin to stodout, but Shoutcast now complains that no data is received and disconnects. I've also tried other variants like "--preset 128" or "-b 64" (with a space), but none seem to actually do anything when invoked from BASS.

QuoteINFO [SRC 172.18.0.1:52566] Shoutcast 2 source connection starting.
WARN [SRC 172.18.0.1:52566 sid=1] Timeout waiting for data
INFO [SRC 172.18.0.1:52566 sid=1] Shoutcast 2 source disconnected.

Encoding a WAV-file manually in command line does work with both "-b 64", "-b64" and "--preset 128".

Regarding FFMPEG, it seems that this too uses LAME at its core, so I don't know why they should produce different results.

Ian @ un4seen

Quote from: GTHvidstenI'm struggling finding a lame.exe, but I did find one that was built for 3.100 (latests lame version), but I can't get it to work.

You can get LAME.EXE from here:

   www.rarewares.org/mp3-lame-bundle.php

Quote from: GTHvidstenstring encCmd = Path.Join(lamePath, "lame.exe") + " -b64 - -";
_encoderHandle = BassEnc.BASS_Encode_Start(
    _mixer,
    encCmd,
    BASSEncode.BASS_ENCODE_NOHEAD,
    null,
    IntPtr.Zero
);

The BASS_ENCODE_NOHEAD flag tells BASS_Encode_Start not to send a WAVE header (containing sample format info) to the encoder. In that case, the command-line should tell the encoder what the sample format is (like your ffmpeg command-line did). For testing purposes, you can simply remove the BASS_ENCODE_NOHEAD flag instead. Also remember to include the BASS_ENCODE_PAUSE flag.

You mentioned BASSenc_MP3 is working. Is there a particular reason that you want to use FFMPEG instead? BASSenc_MP3 is also based on LAME.

GTHvidsten

Ah, forgot about the NOHEAD. I replaced it with DEFAULT and I could get it to work without disconnecting, so Lame is definitely more stable than Ffmpeg. This begs the question, though... what is it with the stdout data stream that Ffmpeg returns that is so radically different from Lame?

The reason I'm using Ffmpeg is that I've experienced more stability in the datastream from it than BASSenc_MP3 (More often audio skips and "bubbling" in BASSenc_MP3). It also supports AAC which is my preferred format in Icecast.

So is there anything that can be done to properly support Ffmpeg? I'd rather keep using it for more stable encoding and AAC support.

Ian @ un4seen

When you tried BASSenc_MP3, do you use the BASS_ENCODE_QUEUE flag? If not, please try that, to move the encoder to its own thread so that it doesn't delay other processing.

There's also a BASSenc_AAC add-on that you could try:

   www.un4seen.com/forum/?topic=18609

GTHvidsten

I'm fairly sure I did try the BASSenc_AAC add-on at one point and finding that to be similar to BASSenc_MP3. I do not think I tried the BASS_ENCODE_QUEUE flag, though, so I'll give this a shot.

I still believe something should be done so that data from ffmpeg can be used, though, as it's quite a popular tool.

Ian @ un4seen

You could try using an ENCODEPROC callback function (in your BASS_Encode_Start call) to write the FFMPEG encoder output to file. And then when the Shoutcast server complains, check if the written file has any errors/corruption (you can use LAME's "--decode" option to do that).

GTHvidsten

I'm not too familiar with what is expected from the decoding process, but maybe you are.
The following is the output from "lame.exe --decode file.mp3", where "file.mp3" is written using this:

private byte[] _encbuffer = new byte[1048510];

private void EncodeProcDataReceived(int handle, int channel, IntPtr buffer, int length, IntPtr user)
{
    Marshal.Copy(buffer, _encbuffer, 0, length);
    using FileStream fs = new("file.mp3", FileMode.Append);
    fs.Write(_encbuffer, 0, length);
}

Quoteinput:  file.mp3
        (44.1 kHz, 2 channels, MPEG-1 Layer III)
output: file.wav  (16 bit, Microsoft WAVE)
skipping initial 529 samples (encoder+decoder delay)
Frame#  186/187    128 kbps  L  R

Ian @ un4seen

That looks like the written MP3 file is fine, as there are no problems reported.

I haven't checked this theory, but perhaps the issue is that FFMPEG's output isn't in whole MP3 frame units and the Shoutcast server doesn't like that (while the Icecast server is fine with it). You could try adding "-flush_packets 1" to your FFMPEG command-line to disable its output buffering.

GTHvidsten

Unfortunately "-flush_packets 1" had no effect :(

Ian @ un4seen

It looks like FFMPEG also has a "-fflags +flush_packets" option that you could try. If that doesn't help either, to confirm whether the "-flush_packets 1" or "-fflags +flush_packets" option is having any effect at all, you could log and compare the EncodeProcDataReceived function's "length" parameter values that you get with and without them. You could also check what values you get when using LAME for comparison.

GTHvidsten

#22
I followed your advice to log the length of the data received in EncodeProcDataReceived and compare ffmpeg to lame.
I can see that ffmpeg with or without the "flush_packets" (either variant) does not vary radically... but compared to lame there's a significant difference.

Here's a sample from ffmpeg: 418, 1254, 835, 1254
Here's a sample from lame: 4096, 4096, 4096, 4096

Quite the difference, and quite the hint as to what might be going on.
I've tried to find a setting to force ffmpeg to always output packages of 4096 bytes, but I haven't found such a setting quite yet (been googling for an hour now).

I've found many packet size settings for various muxers, but non that seem valid for mp3 / adts, so if you know of any way to force the "packet size" in ffmpeg for mp3/adts I'll gladly test it :)

GTHvidsten

If these buffer sizes are indeed the problem, maybe the BassEnc.BASS_Encode_CastInit() could have an optional setting to buffer on its own and only send 4096 blocks to Shoutcast?

Ian @ un4seen

Quote from: GTHvidstenI followed your advice to log the length of the data received in EncodeProcDataReceived and compare ffmpeg to lame.
I can see that ffmpeg with or without the "flush_packets" (either variant) does not vary radically... but compared to lame there's a significant difference.

Here's a sample from ffmpeg: 418, 1254, 835, 1254
Here's a sample from lame: 4096, 4096, 4096, 4096

That actually looks the opposite of what I was theorising :)

It seems LAME is buffering the output and FFMPEG isn't. So perhaps the solution is to enable buffering in FFMPEG. It looks like "-flush_packets 0" should do that.