dgurkaynak / nodeshout

Native libshout bindings for node.js
MIT License
49 stars 19 forks source link

Stream finished, stop html5 playback #9

Closed Konijima closed 2 years ago

Konijima commented 2 years ago

When a stream is finished the html5 stream lose connection so I need to press Play again. The weird part is that sometime it doesn't, so it's as if there is a variable delay between the start of the next stream. Which seem to stop the playback in the player.

I am using this

const fileStream = new FileReadStream('./some/music.mp3', 65536);
const shoutStream = fileStream.pipe(new ShoutStream(shout));

shoutStream.on('finish', () => {
    // I start the next stream here
});

Is there a way to make sure the next stream is starting fast enough to not stop the connection?

Konijima commented 2 years ago

So basically it seems like it doesn't make a continuous stream so the player thinks the song ended. Is it possible to somehow transition from one shoutStream to an other as if it's a continuous stream?

dgurkaynak commented 2 years ago

Hmm, I haven't encountered this kind of issue before. However, I haven't experimented with any HTML5 player; I always used a standalone desktop app like VLC or iTunes. What kind of player are you using?

Konijima commented 2 years ago

Simply an HTML5 audio player, actually using HowlJS.

What I found out is that using the helper class does seem to close the shout when it end so I now use the non-blocking example and instead of closing the shout at the end I simply start streaming an other file and I get seamless transition just as I needed.

Here my working code (issue can be closed)

    StreamAudio(audioFile: string) {
        open(audioFile, 'r', (status, fd) => {
            if (status) return this.error(status.message);

            fstat(fd, (err, stats) => {
                if (err) {
                    return this.error(err.message);
                }

                var fileSize: number = stats.size;
                var bufferSize: number = fileSize;
                var chunkSize = 65536;
                var bytesRead = 0;

                const readStream = () => {
                    var buffer = Buffer.alloc(bufferSize);

                    if ((bytesRead + chunkSize) > fileSize) {
                        chunkSize = (fileSize - bytesRead);
                    }

                    read(fd, buffer, 0, chunkSize, bytesRead, (err, bytesRead_, buffer) => {
                        if (err) {
                            return this.error(err.message);
                        }

                        bytesRead += bytesRead_;

                        if (bytesRead_ > 0) {
                            Object.values(this.shouts).forEach((shout: any) => {
                                shout.send(buffer, bytesRead_);
                            });
                            setTimeout(readStream, Math.abs(Object.values(this.shouts)[0].delay()));
                        } else {
                            closeSync(fd);
                            // my shout is started elsewhere
                            // and closed when I stop my stream elsewhere
                            // so basically I don't close here
                            this.event.emit('shout-completed', audioFile);
                            // I call the next audio to play on the same shout instance
                        }
                    });
                }

                readStream();
                this.event.emit('shout-started', audioFile);
            });
        });
    }

I have a question about shout.free() is there a proper moment that this method should be used if used at all?

dgurkaynak commented 2 years ago

Great news 👍

I have a question about shout.free() is there a proper moment that this method should be used if used at all?

According to libshout documentation, it should be used when you're finished with the shout instance. But to be honest, I've never used it and haven't seen any artifacts. When the node process dies, Icecast server handles it.