Author Topic: Mixing two stream channels and record result to hard disk/stream to web  (Read 5144 times)

kafffee

  • Posts: 276
I had a litttle conversation with Chris off this thread, because the problem was not bass.dll-related. However, we might have worked it out, but I still cannot succeed, because there's another problem that I ran into:

When I do this:

Code: [Select]
EncoderHandle = BassEnc_Mp3.BASS_Encode_MP3_Start(mixer, Nothing, BASSEncode.BASS_ENCODE_LIMIT Or BASSEncode.BASS_ENCODE_AUTOFREE, Nothing, Nothing)
I get the error code 6: Unsupported sample format

Actually I already had this working out, but I must have changed something relevant when I added a third channel to the mixer: A microphone... It's a USB device and actually does not act as a microphone, but as an analog input device, because it has a built-in audio nterface that enables you to connect another analog device via line-in... However, the microphone is a mono device...

I think there might be something wrong with the order that the single commands are in. Here's my code (VB.NET):

Code: [Select]
Option Strict On
Imports Un4seen.Bass                         'the imports
Imports Un4seen.Bass.AddOn.Enc
Imports Un4seen.Bass.AddOn.EncMp3
Imports Un4seen.Bass.AddOn.Mix
Imports Un4seen.Bass.Misc
Imports System.Environment
Imports System.Net

Public Class Form1                     'main class

    Private streamA As Integer                   'declaring the variables
    Private streamB As Integer
    Private mixer As Integer
    Private EncoderHandle As Integer

    Private MikroRecordingProc As RECORDPROC
    Private MikroInput As Integer
    Private MikroOutput As Integer

 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load           'this code is executed when the appplication is loaded

For i = 0 To Bass.BASS_RecordGetDeviceInfos.Count - 1                                                 'this for...next-loop I guess is less relevant, but you never know...
            Dim GeraeteInfo As BASS_DEVICEINFO
            GeraeteInfo = Bass.BASS_RecordGetDeviceInfo(i)
            If GeraeteInfo.type = BASSDeviceInfo.BASS_DEVICE_TYPE_MICROPHONE Then

                Debug.WriteLine("Mikrofon: " & GeraeteInfo.name.ToString)
            End If

            If GeraeteInfo.type = BASSDeviceInfo.BASS_DEVICE_TYPE_LINE Then

                Debug.WriteLine("Line-In: " & GeraeteInfo.name.ToString)
            End If

            If GeraeteInfo.type = BASSDeviceInfo.BASS_DEVICE_TYPE_HEADSET Then

                Debug.WriteLine("Headset: " & GeraeteInfo.name.ToString)
            End If
            If GeraeteInfo.type = BASSDeviceInfo.BASS_DEVICE_TYPE_DIGITAL Then

                Debug.WriteLine("Digitaler Eingang: " & GeraeteInfo.name.ToString)
            End If

            If GeraeteInfo.type = BASSDeviceInfo.BASS_DEVICE_TYPE_SPDIF Then

                Debug.WriteLine("SPDIF: " & GeraeteInfo.name.ToString)
            End If
        Next

Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, Me.Handle) 'initializing output device
       
        mixer = Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_StreamCreate(44100, 3, BASSFlag.BASS_DEFAULT)   'setting up the mixer channel
        Bass.BASS_ChannelSetAttribute(mixer, BASSAttribute.BASS_ATTRIB_BUFFER, 0) ' disable playback buffering

        Bass.BASS_RecordInit(-1)  'initializing input device
        MikroRecordingProc = New RECORDPROC(AddressOf MikroRecordingCallback)        'handling the "microphone stuff"
        MikroOutput = Bass.BASS_StreamCreate(44100, 1, BASSFlag.BASS_DEFAULT, BASSStreamProc.STREAMPROC_PUSH)
        MikroInput = Bass.BASS_RecordStart(44100, 1, BASSFlag.BASS_STREAM_DECODE, MikroRecordingProc, IntPtr.Zero)
        BassMix.BASS_Mixer_StreamAddChannel(mixer, MikroOutput, BASSFlag.BASS_MIXER_NORAMPIN Or BASSFlag.BASS_STREAM_AUTOFREE)


        Bass.BASS_ChannelPlay(mixer, False)           'playing the mixer

End Sub

Public Function MikroRecordingCallback(ByVal handle As Integer, ByVal buffer As IntPtr, ByVal length As Integer, ByVal user As IntPtr) As Boolean          'microphone callback function
        Bass.BASS_StreamPutData(MikroOutput, buffer, length)
        Bass.BASS_ChannelPlay(MikroOutput, False)    'I'm not sure about this line, maybe this has to do with the issue??
                                                                             'Because the other channels being put in the mix are not explicitely told to playback; but when I skip this line, I still get the same error "Unsupported sample format and I will not hear the mic on speakers...
        Return True
    End Function

Private Sub btnPlayFile1_Click(sender As Object, e As EventArgs) Handles btnPlayFile1.Click       'play file 1 on click
        streamA = Bass.BASS_StreamCreateFile("C:\Users\alpha\Music\dancehall\Macka B\01 Macka B - 01.mp3", 0, 0, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_PRESCAN Or BASSFlag.BASS_STREAM_DECODE)
        Debug.WriteLine("streamAhandle:" & CStr(streamA) & "Fehler:" & CStr(Bass.BASS_ErrorGetCode))
        Dim success As Boolean = BassMix.BASS_Mixer_StreamAddChannel(mixer, streamA, BASSFlag.BASS_MIXER_NORAMPIN Or BASSFlag.BASS_STREAM_AUTOFREE)
        Debug.WriteLine(CStr(success))
    End Sub

    Private Sub btnPlayFile2_Click(sender As Object, e As EventArgs) Handles btnPlayFile2.Click            play file 2 on click
        streamB = Bass.BASS_StreamCreateFile("C:\Users\alpha\Music\pop\Clueso\Weit Weg\14 Chicago.mp3", 0, 0, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_PRESCAN Or BASSFlag.BASS_STREAM_DECODE)
        Debug.WriteLine("streamBhandle:" & CStr(streamB) & "Fehler:" & CStr(Bass.BASS_ErrorGetCode))
        Dim success As Boolean = BassMix.BASS_Mixer_StreamAddChannel(mixer, streamB, BASSFlag.BASS_MIXER_NORAMPIN Or BASSFlag.BASS_STREAM_AUTOFREE)
        Debug.WriteLine(CStr(success))
    End Sub


Private Sub btnMixToHD_Click(sender As Object, e As EventArgs) Handles btnMixToHD.Click       'Start th encoder on click...
EncoderHandle = BassEnc_Mp3.BASS_Encode_MP3_Start(mixer, Nothing, BASSEncode.BASS_ENCODE_LIMIT Or BASSEncode.BASS_ENCODE_AUTOFREE, Nothing, Nothing)
        MessageBox.Show("EncoderHandle: " & CStr(EncoderHandle))
        MessageBox.Show("Errorcode: " & CStr(Bass.BASS_ErrorGetCode))

End Sub

Private Sub btnStreamToServer_Click(sender As Object, e As EventArgs) Handles btnStreamToServer.Click     'start streaming on click
Dim Erfolg As Boolean = BassEnc.BASS_Encode_CastInit(EncoderHandle, "xx.xxx.xxx.xxx:8000/stream.mp3", "hackme", BassEnc.BASS_ENCODE_TYPE_MP3, "MeinErsterStream", Nothing, "MeinGenre", "MeineBeschreibung", Nothing, 128, True)
        If Erfolg = True Then
            MessageBox.Show("erfolgreich")
        Else
            MessageBox.Show("nicht erfolgreich")
        End If
        MessageBox.Show("Fehler: " & CStr(Bass.BASS_ErrorGetCode))

End Sub

End Class

Ian @ un4seen

  • Administrator
  • Posts: 26102
The issue is that the chans=3 parameter in the BASS_Mixer_StreamCreate call means that the mixer output will have 3 channels, while MP3 doesn't support more than 2 (stereo), so BASS_Encode_MP3_Start fails with BASS_ERROR_FORMAT. Did you intend for the mixer output to have 3 channels, or did you just mean to say that the mixer will have 3 sources? If the latter, you don't need to tell the mixer how many sources there will be - you can add as many as you like, when you like.

kafffee

  • Posts: 276
Ah okay, that was it. I misunderstood the docs - I thought the chans-Argument was supposed to pass in the amount of sources...

And for the other issue: I now managed to stream to other devices in my (W)LAN even with all of my firewalls being turned on. I passed the local IP of my computer to Icecast (both in the config file and when calling _CastInit). Also, I granted Icecast.exe access to incoming and outcoming connections in my firewall setting, forwarded port 8000 in my firewall and router settings, and Switched my (second) WLAN Router to repeater Mode, whereas I havent figured out yet, if the latter two ones
are really necessary... I'll keep you up to date...

Next step would be to stream worldwide via internet. Is this even doable without any third-party-icecast-hosts? Or is the only problem my upload-speed? I tried to pass my external IP to icecast in the config file and the _CastInit-call, but I get an error no. 40 - connection timedout when I try to play the stream on any device...

My external IP I got from http://ifconfig.me/ip
« Last Edit: 22 Apr '22 - 16:46 by kafffee »

Ian @ un4seen

  • Administrator
  • Posts: 26102
You can/should use the server's local IP address in the BASS_Encode_CastInit call. It's only the outside listeners that would need to use the external IP address.

kafffee

  • Posts: 276
I just read in the Icecast faqs that IP addresses in the <hostname></hostname> tag in the configuration file are invalid. You would have to register a DNS name...  :(

Quote
This is the public hostname of your Icecast. Your server is reachable by this domain name. It is used in a few cases Icecast needs to know how it is reachable from the outside. One such case is for yp / directory submission.

You should set this to a DNS name that has at least a A, or AAAA resource record pointing to this instance. Using of IP addresses, or (website) URIs is invalid.
« Last Edit: 22 Apr '22 - 19:44 by kafffee »

Ian @ un4seen

  • Administrator
  • Posts: 26102
It'll probably be fine to use an IP address instead of a hostname (I see some using an IP address listed at http://dir.xiph.org/), but if you want to get a hostname you could use a dynamic DNS service.

kafffee

  • Posts: 276
I am not getting this done, whatever I do... I used different versions of shoutcast and icecast, console, GUI, whatsoever. I had my ISP give me a static IPv4, since he used Dual Stack Tunneling... Nothing...

As a conclusion, why not try it again with Bass_Encode_ServerInit, at least then I do not depend on any config files or external programs... So there's not that much to do wrong...

Currently I am using this:

Code: [Select]
Dim Portnumber As Integer = BassEnc.BASS_Encode_ServerInit(EncoderHandle, "xxx.xxx.xxx.xxx:0", 64000, 64000, BASSEncodeServer.BASS_ENCODE_SERVER_NOHTTP, Nothing, IntPtr.Zero)
        MessageBox.Show(CStr(Portnumber))

Is there anything to do better? As an IP, I use the local IPv4 of the machine that the program is running on. It only works in my (W)LAN, but there must be some way to get this done over internet, right? I tried any of my IP addresses though to connect from outside (external IPv4; external, temporary and local IPv6) but there is no connection. Is there anything else I could try? When I want to forward ports, I have to enter the IPv4 of the machine the server is runnung on right? Do I have to use TCP our UDP in the port forwarding settings?

I'd be really glad if someone could help me...

Ian @ un4seen

  • Administrator
  • Posts: 26102
Unless you happen to have multiple network interfaces, you generally shouldn't include an IP address in the BASS_Encode_ServerInit "port" parameter, only a port number. You shouldn't leave BASSenc to choose a random port though (ie. don't use "0") because you will need to set your router to forward incoming TCP traffic on that port to the server.

kafffee

  • Posts: 276
Wow thats awesome, so I don't even have to worry about my user entering the wrong IP address respectively I don't have to get it programmatically...

But what do you mean by multiple network interfaces? Sorry I am not a native English speaker as you may have noticed...

Do you mean like more than one network interface card? Or more like two routers?

By the way, thanks to Chris for his patience we had a conversation via eMail the past couple of days, with the result that now I can stream over the internet. I just had to forward the port that I am using to the local IPv4 of my machine in the firewall and router settings and set my DMZ-Host to the local IPv4 address of my machine.... That was all I needed  :) But watch out: This will open all (!) of your ports!

Two more questions:

(1) When I use _ServerInit, is the value for burst and buffer (64000) okay and how do I know whether it is?
(2) I couldnt really find any further information about the flags in the docs. Could you explain me what BASS_ENCODE_SERVER_DEFAULT, BASS_ENCODE_SERVER_NOHTTP and BASS_ENCODE_SERVER_META are doing?

Thanks in advance for the effort  :D
« Last Edit: 28 Apr '22 - 15:46 by kafffee »

Chris

  • Posts: 2217
BASS_ENCODE_SERVER_META   Send Shoutcast metadata to clients that request it.
BASS_ENCODE_SERVER_NOHTTP   Do not read or send HTTP headers.
BASS_ENCODE_SERVER_DEFAULT (I did not find something about this entry (i think that Value  special for Net)

Ian @ un4seen

  • Administrator
  • Posts: 26102
But what do you mean by multiple network interfaces? Sorry I am not a native English speaker as you may have noticed...

Do you mean like more than one network interface card? Or more like two routers?

Yes, I meant multiple network cards/connections. In that case, you might use an IP address to limit which connection is used by the server.

(1) When I use _ServerInit, is the value for burst and buffer (64000) okay and how do I know whether it is?

That should be fine. You can calculate a value to use based on the bitrate of the data you'll be serving. For example, 128kbps is 16000 bytes per second, so 64000 bytes would cover 4 seconds.

kafffee

  • Posts: 276
I have some other questions concerning hard disk recording and streaming:

How can I stop either?:

I started streaming with BassEnc_Mp3.BASS_Encode_MP3_Start and BASS_Encode_ServerInit

I started recording with BassEnc_Mp3.BASS_Encode_MP3_StartFile

Can I use Broadcast class together with _ServerInit? For instance for getting the amount of connected listeners...

Chris

  • Posts: 2217
which Broadcast Class?
The Amount of Listeners can you get via the EncodeClientproc which was  created in the Server_init call.
http://www.un4seen.com/doc/#bassenc/ENCODECLIENTPROC.html

kafffee

  • Posts: 276
@Chris:

This one:

http://www.radio42.com/bass/help/html/b8533cef-2485-af79-61aa-1b4a637099b9.htm

I will try with EncodeClientproc and check back...

kafffee

  • Posts: 276
OK I took a look at EncodeClientProc and I must admit that I have no idea how to use it...

Is there something like a sample code except for the one in the docs, preferredly VB/C#??

Are the arguments passed automatically when called by _ServerInit?
« Last Edit: 4 May '22 - 14:30 by kafffee »

Chris

  • Posts: 2217
So something like this

Code: [Select]
var listeners : DWORD = 0;

function ClientProc(Encoder : HENCODE; connect : boolean; Client:PAnsiChar; Headers:PAnsiChar; User:Pointer): boolean; stdcall;
begin
  // the Client will return  IP address and port number
  if connect then
  begin
    inc(listeners);
   //  if you want to update the listeners Value e.g in Label just do it here via postmessage .....
  else
  begin
     dec(listeners);
   //  if you want to update the listeners Value e.g in Label just do it here via postmessage .....
  end;
end;

BASS_Encode_ServerInit(encoder, "8000", 64000, 64000, 0,ClientProc, NULL);

kafffee

  • Posts: 276
Is this Delphi? I can only guess...

So far I got this:


Code: [Select]
Public class Form1
Private EnocdeClientProc As ENCODECLIENTPROC

Private Sub btnStartStreaming_Click(sender As Object, e As EventArgs) Handles btnStartStreaming.Click
[...]
Dim Portnumber As Integer = BassEnc.BASS_Encode_ServerInit(StreamEncoderHandle, "8000", 64000, 64000, 0, EncodeClientProc, IntPtr.Zero)
End Sub

Private Function EncodeClientProc(ByRef handle As Integer, ByRef connect As Boolean, ByRef client As String, ByRef headers As String, ByRef users As IntPtr) As Boolean

End Function
End Class

But what I get is an error in the _ServerInit call when passing EncodeClientProc:

There are arguments missing....

So where does the function get its arguments from?
« Last Edit: 6 May '22 - 15:18 by kafffee »

Ian @ un4seen

  • Administrator
  • Posts: 26102
There is example code for counting listeners (among other things) in BASS.Net's ENCODECLIENTPROC documentation. Reducing that to only counting listeners, it would look like this:

Code: [Select]
int _listeners = 0; // client count

private bool EncodeClientProc(int handle, bool connect, string client, IntPtr headers, IntPtr user)
{
   if (connect)
   {
       // increment the client count
       _listeners++;
   }
   else
   {
       // decrement the client count
       _listeners--;
   }
   return true;
}

Also see the "Callbacks and Delegates" section of the "Interoperating with Unmanaged Code" page in the BASS.Net documentation for information on using callback functions (like ENCODECLIENTPROC) in general.

kafffee

  • Posts: 276
Ok I read through the "Interoperating with Unmanaged Code" section and did as described, but still it doesn't work... But there is a broken link here, so I could not read all of the relevant information I guess... Right a the beginning, at (see Delegate). Its pointing to a microsoft docs page which does not exist...

http://www.bass.radio42.com/help/html/9b9af3f1-f0dd-42e8-898a-ed607b9d0f60.htm#Callbacks

My code so far:

Code: [Select]
Public Class Form1

Private EnocdeClientProc As ENCODECLIENTPROC = New ENCODECLIENTPROC(AddressOf EncodeClientProc)

Private Sub btnStartStreaming_Click(sender As Object, e As EventArgs) Handles btnStartStreaming.Click
Dim Portnumber As Integer = BassEnc.BASS_Encode_ServerInit(StreamEncoderHandle, "8000", 64000, 64000, 0, EncodeClientProc, IntPtr.Zero)
End Sub

Private Function EncodeClientProc(ByVal handle As Integer, ByVal connect As Boolean, ByVal client As String, ByVal headers As String, ByVal users As IntPtr) As Boolean

End Function
End Class

Edit: Oh sry I have overlooked a mistake...
I will try another way...

Edit2: OK I solved it. Two other questions:

How can I stop recording to disc respectively streaming independently from each other?

Just use _ServerKick method or should I do something else for exiting the stream?

How to stop recording to disc without ending the stream/broadcast?

« Last Edit: 8 May '22 - 19:03 by kafffee »

Ian @ un4seen

  • Administrator
  • Posts: 26102
To be able to stop encoding to a file and server independently, you will need to use a separate encoder for each one, which allows you to use BASS_Encode_Stop on each one separately.

kafffee

  • Posts: 276
Ok I think this thread can be closed  :D

kafffee

  • Posts: 276
I have to go into it again because there are some other questions that I have:

Currently I am recording/streaming with:

Code: [Select]
StreamEncoderHandle = BassEnc_Mp3.BASS_Encode_MP3_Start(mixer, Nothing, BASSEncode.BASS_ENCODE_LIMIT Or BASSEncode.BASS_ENCODE_AUTOFREE, Nothing, Nothing)
and

Code: [Select]
RecordingEncoderHandle = BassEnc_Mp3.BASS_Encode_MP3_StartFile(mixer, Nothing, BASSEncode.BASS_ENCODE_AUTOFREE, "F:\Ablage\TestRecordedStream.mp3")
and I am doing this:

Code: [Select]
mixer = Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_DEFAULT Or BASSFlag.BASS_SAMPLE_FLOAT)
Bass.BASS_ChannelSetAttribute(mixer, BASSAttribute.BASS_ATTRIB_BUFFER, 0) ' disable playback buffering'
Bass.BASS_ChannelPlay(mixer, False)

I start playing with:

Code: [Select]
streamA = Bass.BASS_StreamCreateFile("C:\Test.mp3", 0, 0, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_PRESCAN Or BASSFlag.BASS_STREAM_DECODE)
        BassMix.BASS_Mixer_StreamAddChannel(mixer, streamA, BASSFlag.BASS_MIXER_NORAMPIN Or BASSFlag.BASS_STREAM_AUTOFREE)

I also have other channels in the same manner, like streamB and a microphone:

Code: [Select]
MikroInput = Bass.BASS_RecordStart(44100, 1, BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_SAMPLE_FLOAT, Nothing, IntPtr.Zero)
BassMix.BASS_Mixer_StreamAddChannel(mixer, MikroInput, BASSFlag.BASS_MIXER_NORAMPIN Or BASSFlag.BASS_STREAM_AUTOFREE)

So the question is:
How can I repeatedly start/pause/stop my source channels without the recording/streaming being affected, so the recording/streaming should just keep on going no matter whether the channels are stopped or paused?

I also want to be able to remove source channels from the mix in order to play them on another device without any skips and backwards.

Ian @ un4seen

  • Administrator
  • Posts: 26102
That sounds like adding the BASS_MIXER_NONSTOP flag to your BASS_Mixer_StreamCreate call is what you want - it'll keep the mixer going even when no sources are active.

kafffee

  • Posts: 276
Ok so when I do this, I can play/pause/stop my source channels on the channel itself? For instance BASS_ChannelPlay/Pause(streamA)) whenever I want instead of BASS_ChannelPlay(mixer)? Or will this end up in not hearing my mixer channel?
I am wondering because currently, I am only calling BASS_ChannelPlay(mixer) without calling (BASS_ChannelPlay(streamA), and streamA starts playing automatically when I add it to the mixer channel...
« Last Edit: 21 Jun '22 - 14:40 by kafffee »

Ian @ un4seen

  • Administrator
  • Posts: 26102
You would still need to use BASS_Mixer_StreamAddChannel to play through a mixer, as BASS_ChannelPlay only applies to normal BASS playback. Here's a summary:

   play = BASS_Mixer_StreamAddChannel
   stop = BASS_Mixer_ChannelRemove
   pause = BASS_Mixer_ChannelFlags with BASS_MIXER_CHAN_PAUSE
   resume = BASS_Mixer_ChannelFlags without BASS_MIXER_CHAN_PAUSE

You can also use BASS_MIXER_CHAN_PAUSE with BASS_Mixer_StreamAddChannel to start in a paused state. Note BASS.Net doesn't currently include BASS_MIXER_CHAN_PAUSE, but you can use BASS_MIXER_PAUSE instead (the older name for the same flag).