Roboroads
Roboroads

Reputation: 1709

Streaming mp3 using node http to Icecast server gives no output

I'm trying to stream a custom mp3 datastream to Icecast2. Icecast recognises there's a mountpoint, however the streamed data won't play and I have no idea why.

import ffmpeg from 'fluent-ffmpeg';
import { request } from 'node:http';

const req = request(
  {
    host: 'localhost',
    port: 8000,
    path: '/radio',
    method: 'PUT',
    auth: 'source:password',
    headers: {
      'Content-Type': 'audio/mpeg',
      Expect: '100-continue',
      'Ice-Public': 0,
      'Ice-Name': 'Testing Stream!',
      'Ice-Description': 'Testing Stream!',
      'Ice-Genre': 'Various',
      'Icy-MetaData': 1
      //'Icy-MetaInt': metaInt
    }
  },
  (res) => {
    console.log('Server responded with status code:', res.statusCode);
    res.on('data', (chunk) => {
      console.log('Response chunk:', chunk.toString());
    });
  }
);

req.on('error', (err) => {
  console.error('Error streaming to Icecast server:', err);
});

req.on('close', () => {
  console.error('Icecast server closed connection');
  process.exit(1);
});

req.on('continue', () => {
  console.log('Continue event received');
  ffmpeg({ source: 'test.mp3' })
    .native()
    .outputOption(['-map 0:a'])
    .audioBitrate(320)
    .audioCodec('libmp3lame')
    .audioFrequency(44100)
    .audioChannels(2)
    .format('mp3')
    .on('progress', (progress) => {
      console.log('Is open:' + req.socket.writable + ' - ' + progress.timemark);
    })
    .on('start', function (commandLine) {
      console.log('Spawned Ffmpeg with command: ' + commandLine);
    })
    .on('end', () => {
      console.log('Processing finished !');
      req.end();
    })
    .on('error', (err) => {
      console.error('FFmpeg error:', err);
    })
    .pipe(req);
});

Note that when I try to do this with ffmpeg using the icecast protocol, it works without issues: ffmpeg -re -i test.mp3 -b:a 320k -acodec libmp3lame -ar 44100 -ac 2 -map 0:a -f mp3 icecast://source:password@localhost:8000/radio. It looks like something is not right with request.

Upvotes: 1

Views: 38

Answers (1)

Roboroads
Roboroads

Reputation: 1709

I implemented a lower-lever version, using createConnection instead of going through the request. This works.

import ffmpeg from 'fluent-ffmpeg';
import { Writer } from 'icy';
import { createReadStream, createWriteStream } from 'node:fs';
import { request } from 'node:http';
import { createConnection } from 'node:net';

const METAINT = 8192;

const socket = createConnection(8000, '127.0.0.1', () => {
  let auth = Buffer.from('source:password').toString('base64');
  let headers = `PUT /radio HTTP/1.1\r\n`;
  headers += `Host: 127.0.0.1:8000\r\n`;
  headers += `Authorization: Basic ${auth}\r\n`;
  headers += `User-Agent: Streamline/0.1\r\n`;
  headers += `Content-Type: audio/mpeg\r\n`;
  headers += `Expect: 100-continue\r\n`;
  headers += `Ice-Public: 0\r\n`;
  headers += `Ice-Name: Testing Stream\r\n`;
  headers += `Ice-Description: Testing Stream\r\n`;
  headers += `Ice-Genre: Various\r\n`;
  headers += `Icy-MetaInt: ${METAINT}\r\n`;
  headers += `\r\n`;

  socket.write(headers);

  socket.on('data', (data) => {
    if (data.toString().includes('HTTP/1.1 100 Continue')) {
      console.log('Continue event received');

      const icyWriter = Writer(METAINT, {});

      ffmpeg(createReadStream('test.mp3'))
        .native()
        .outputOptions(['-map 0:a'])
        .audioCodec('libmp3lame')
        .audioBitrate(320)
        .audioFrequency(44100)
        .audioChannels(2)
        .format('mp3')
        .on('progress', (progress) => {
          console.log('Is open:' + socket.writable + ' - ' + progress.timemark);
        })
        .on('start', (commandLine) => {
          console.log('Spawned Ffmpeg with command: ' + commandLine);
        })
        .on('end', () => {
          console.log('Processing finished!');
          socket.end();
        })
        .on('error', (err) => {
          console.error('FFmpeg error:', err);
        })
        .pipe(icyWriter);

      icyWriter.on('data', (chunk) => {
        socket.write(chunk);
      });

      setInterval(() => {
        icyWriter.queue('Alice DJ - Better Off Alone');
      }, 5000);
    }
  });
});

Upvotes: 0

Related Questions