native-bindings / libshout

libshout C++ bindings to Node.js
https://github.com/xiph/Icecast-libshout
3 stars 2 forks source link

browser compatibility #1

Open lmick002 opened 3 years ago

lmick002 commented 3 years ago

Is it possible to use this package in a browser environment?

VictorQueiroz commented 2 years ago

@lmick002 I don't think so. Maybe if you compile libshout itself using EMScripten, but I doubt it'd work out-of-the-box.

starapple2 commented 1 year ago

I'm not clear about usage. If it's not in a browser how do you use this package?

VictorQueiroz commented 1 year ago

@starapple2 You should use it in a Node.js environment. It's a shortcut to the original xiph/Icecast-libshout that's written in C.

libshout

Libshout is a library for communicating with and sending data to an icecast server. It handles the socket connection, the timing of the data, and prevents bad data from getting to the icecast server.

With just a few lines of code, a programmer can easily turn any application into a streaming source for an icecast server. Libshout also allows developers who want a specific feature set (database access, request taking) to concentrate on that feature set, instead of worrying about how server communication works.

Please refer to the api reference and example code to start learning how to use libshout in your own code.

Libshout is licensed under the LGPL. Please see the COPYING file for details.

If you have any questions or comments, please visit us at http://www.icecast.org or email us at team@icecast.org.

starapple2 commented 1 year ago

@VictorQueiroz, thanks for your response. So I can include it in a project and connect to it from the browser via websockets, is that it? I am trying to get clarification on what I think @lmick002 was asking.

VictorQueiroz commented 1 year ago

@starapple2 I think the server uses TCP sockets. If I am correct, the best final solution would be to have a web socket server, that will be a proxy between the front-end and the TCP server, as it will do the translation for you.

For example:

website <> web socket server <> tcp server
starapple2 commented 1 year ago

@VictorQueiroz, aha. Beyond the simplicity I envisaged. As is, what would be a use case? A Node.js dedktop app?

VictorQueiroz commented 1 year ago

@starapple2 In my case, I used it for creating a Node.js coimmand-line tool that would transmit the microphone audio to an Icecast server, so I could listen it from distance.

starapple2 commented 1 year ago

@VictorQueiroz, in my case I want to send the mic from the browser window to Icecast.

VictorQueiroz commented 1 year ago

@starapple2 Then create the web socket server using a Node.js server and use the library to transmit whatever is sent to the websocket from the browser.

You can also use a WebWorker to avoid blocking the UI thread.

starapple2 commented 1 year ago

@VictorQueiroz, I have created a server and have been looking for the correct libshout package to integrate and receive the connection from the client JavaScript. Each library I've seen appears to want a file rather than a blob that the script generates.

VictorQueiroz commented 1 year ago

@starapple2 Steps I suggest are:

  1. Create a web socket server using a Node.js server (install libshout)
  2. Send the encoded data (anything your Icecast server supports) of the microphone to the server (I suggest the OPUS codec. You can use this to encode the data to the OPUS format)
  3. In the web socket server, whatever comes in as an ArrayBuffer, sends to the icecast server using libshout

It's either this, or add WebSocket support for the icecast server, which is not the best choice IMHO.

starapple2 commented 1 year ago

@VictorQueiroz I've just added libshout to the project and will see what's what. I am looking at the Readme and seeing a const to a directory path to a file so I imagine that would need to be amended for what is sent from the encoder.

VictorQueiroz commented 1 year ago

@starapple2 I think you can send the file by chunks however you want and the Icecast server clients will be able to play it as soon as there's something that can be decoded.

This library behaves exactly as the original libshout library. It even has the same methods, so you can just read the libshout documentation for more information about usage of the library.

If any method is missing, let me know, and I can add it quickly. I plan to add asynchronous functionalities to it later.

starapple2 commented 1 year ago

@VictorQueiroz , I added opus-codec to the project. Is it then redundant based on what you said just now?

This is what I had originally written for some library shout:

const shout = require('shout');

const stream = shout.createStream({
  host: 'my-server.com',
  port: 8000,
  user: 'username',
  password: 'password',
  mount: '/stream',
  name: 'My Stream',
  genre: 'Talk, Reggae',
});

This part I need to be clear about:

// Read audio data from source and write it to the stream
 const audioData = readAudioDataFromFile();
 stream.write(audioData);

// Close the stream
 stream.end();
VictorQueiroz commented 1 year ago

@starapple2 Seems about right, but read this from my opus library to be able to use it properly. For now, you can try on the UI thread first for simplicity, but the audio decoding is supposed to be done in a WebWorker to avoid blocking the UI thread. Otherwise, you might have cropped milliseconds.

VictorQueiroz commented 1 year ago

@starapple2 There's this app I developed using this opus codec library, you can take a look at it:

https://github.com/rectimeproject/web

It might help you on audio decoding.

starapple2 commented 1 year ago

@VictorQueiroz I have been using vanilla JavaScript to save a recording from an online example. I'm not familiar with React and most of these libraries as most of my Web activity is a bit of PHP and occasionally some simple JavaScript.

starapple2 commented 1 year ago

@VictorQueiroz, I can't seem to initialize libshout using "require" and "import" isn't usable in this context. I keep getting Shout is not defined.

VictorQueiroz commented 1 year ago

@starapple2 In the code you sent, you're using shout package, not libshout. Can you try with libshout and see?

starapple2 commented 1 year ago

I replaced with libshout from the start.

On Mon, Jul 3, 2023, 1:26 p.m. Victor Queiroz @.***> wrote:

@starapple2 https://github.com/starapple2 In the code you sent, you're using shout package, not libshout. Can you try with libshout and see?

— Reply to this email directly, view it on GitHub https://github.com/VictorQueiroz/libshout/issues/1#issuecomment-1618917155, or unsubscribe https://github.com/notifications/unsubscribe-auth/AIAE2PJA6MLJVAUHXCWJHXDXOL6DHANCNFSM5HWEXICA . You are receiving this because you were mentioned.Message ID: @.***>

VictorQueiroz commented 1 year ago

@starapple2 Can you provide more information, like, what Node.js version you're using?

starapple2 commented 1 year ago

@VictorQueiroz it could just be that I'm doing something wrong:

const express = require('express')
const stream = require('libshout')
const shout = new Shout();
const fs = require('fs')
const { WebSocketServer } = require('ws')
const port = 3000

const app = express()

app.use(express.static('public'))

stream.shout({
  setHost: 'localhost',
  setProtocol: (0),
  setPort: 8000,
  setUser: 'source',
  setPassword: 'MyPassW',
  setMount: '/stream.mp3',
  setName: 'My Stream',
  setGenre: 'Talk, Reggae',
});

The error: const shout = new Shout(); ^ ReferenceError: Shout is not defined

It's Node v14.21.2.

starapple2 commented 1 year ago

That accent (^) is actually pointed at "new".

VictorQueiroz commented 1 year ago

@starapple2 Try this:

const { Shout } = require('libshout');
const s = new Shout();
s.setHost('localhost');
s.setProtocol(0);
s.setPort(8000);
s.setUser('source');
s.setPassword('MyPassW');
s.setMount('/stream.mp3');

Also notice that there is no shout method in the Shout.prototype, so you must read this before using the library.

starapple2 commented 1 year ago

@VictorQueiroz, the error is: const s = new Shout();                  ^ TypeError: Shout is not a constructor

VictorQueiroz commented 1 year ago

@starapple2 Try this:

const { Shout } = require('libshout');
const s = new Shout();
s.setHost('localhost');
s.setProtocol(0);
s.setPort(8000);
s.setUser('source');
s.setPassword('MyPassW');
s.setMount('/stream.mp3');

Also notice that there is no shout method in the Shout.prototype, so you must read this before using the library.

@starapple2 Check the require line. It's different.

starapple2 commented 1 year ago

@VictorQueiroz, are you sure? What's the difference? The error message is the same.

VictorQueiroz commented 1 year ago

@starapple2 Did you try what I suggested?

starapple2 commented 1 year ago

Sorry, I missed what you suggested @VictorQueiroz . Did you mean the usage example? TYhis was the message using that example: (node:98226) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. (Use node --trace-warnings ... to show where the warning was created) /home/mark/Applications/abeng/index.js:11 import {Shout} from 'libshout'; ^^^^^^

SyntaxError: Cannot use import statement outside a module

VictorQueiroz commented 1 year ago

@starapple2 No. I sent you some code here, please check and let me know. Here: https://github.com/VictorQueiroz/libshout/issues/1#issuecomment-1619030915

starapple2 commented 1 year ago

Ahh. I tried that and posted the error @VictorQueiroz:

const s = new Shout();                  ^ TypeError: Shout is not a constructor

VictorQueiroz commented 1 year ago

@starapple2 Sorry about the delay. I think the README is wrong, but it should work like that by default, so I will change the behavior to be const {Shout} = require("libshout"). For now you can import it like this:

const Shout = require('libshout');
starapple2 commented 1 year ago

Thanks @VictorQueiroz, you've got it. Now no errors and console says "App listening at http://localhost:3000". I'll now muddle through sending data from the client, amending the code to expect data rather than searching a folder for a file.

starapple2 commented 1 year ago

@VictorQueiroz, do you have an example, or can you write one, of using a playlist? I generate a playlist from files staged for upload and use the file url as their link since JavaScript cannot provide local paths in the file system. I'll work on accessing the mic stream after sorting out a playlist.