MP3 won't stream with JMF

5k views Asked by At

Basic idea is to access .mp3 file and send it through RTP stream to other client, who will want to play that song.

Here is RTPServer.java, which I found online and modified it to my liking.

    package server;

import java.net.InetAddress;
import javax.media.rtp.*;
import javax.media.rtp.rtcp.*;
import javax.media.*;
import javax.media.protocol.*;
import javax.media.control.*;

public class RTPServer implements ControllerListener, Runnable {
    private boolean realized = false;
    private boolean configured = false;
    private String ipAddress;
    Processor p;
    MediaLocator src;

    public static void main (String[] args) {
        RTPServer rtp = new RTPServer("192.168.1.101", "04 - Blue.mp3");
        Thread t = new Thread(rtp);
        t.start();
    }

    public RTPServer(String ip, String song) {
        ipAddress = ip;
 String srcFile = "Muzika\\" + song;
 src = new MediaLocator("file:" + srcFile);

    }

    private void setTrackFormat(Processor p) {
 // Get the tracks from the processor
 TrackControl [] tracks = p.getTrackControls();
 // Do we have atleast one track?
 if (tracks == null || tracks.length < 1) {
     System.out.println("Couldn't find tracks in processor");
     System.exit(1);
 }

 // Set the output content descriptor to RAW_RTP
 // This will limit the supported formats reported from
 // Track.getSupportedFormats to only valid RTP formats.
 ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP);
 p.setContentDescriptor(cd);

 Format supported[];
 Format chosen;
 boolean atLeastOneTrack = false;

 // Program the tracks.
 for (int i = 0; i < tracks.length; i++) {
     Format format = tracks[i].getFormat();
            System.out.println("Trenutni format je " +format.getEncoding());
     if (tracks[i].isEnabled()) {
  supported = tracks[i].getSupportedFormats();
  for (int n = 0; n < supported.length; n++)
      System.out.println("Supported format: " + supported[n]);

  // We've set the output content to the RAW_RTP.
  // So all the supported formats should work with RTP.
  // We'll just pick the first one.

  if (supported.length > 0) {
      chosen = supported[0]; // this is where I tried changing formats
      tracks[i].setFormat(chosen);
      System.err.println("Track " + i + " is set to transmit as: " +chosen);
      atLeastOneTrack = true;
  } else
      tracks[i].setEnabled(false);
     } else
  tracks[i].setEnabled(false);
 }
    }

    private void transmit(Processor p) {
 try {
     DataSource output = p.getDataOutput();
     PushBufferDataSource pbds = (PushBufferDataSource) output;
     RTPManager rtpMgr = RTPManager.newInstance();
     SessionAddress localAddr, destAddr;
     SendStream sendStream;
     int port = 42050;
     SourceDescription srcDesList[];
     localAddr = new SessionAddress( InetAddress.getLocalHost(), port);
     InetAddress ipAddr = InetAddress.getByName(ipAddress);
     destAddr = new SessionAddress( ipAddr, port);
     rtpMgr.initialize(localAddr);
     rtpMgr.addTarget(destAddr);
     sendStream = rtpMgr.createSendStream(output, 0);
     sendStream.start();
     System.err.println( "Created RTP session: " + ipAddress + " " + port);
     p.start();
 } catch(Exception e) {
     e.printStackTrace();
 }
    }

    public synchronized void controllerUpdate(ControllerEvent evt) {
 if (evt instanceof RealizeCompleteEvent) {
     realized = true;
 } else  if (evt instanceof ConfigureCompleteEvent) {
     configured = true;
 } else if (evt instanceof EndOfMediaEvent) {
     System.exit(0);
 } else {
     // System.out.println(evt.toString());
 }
    }

    public void run() {

 try {
     p = Manager.createProcessor(src);
     p.addControllerListener(this);
     p.configure();
     while (! configured) {
  try {
      Thread.currentThread().sleep(100L);;
  } catch (InterruptedException e) {
      // ignore
  }
     }

     setTrackFormat(p);
     p.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW_RTP));

     p.realize();
     while (! realized) {
  try {
      Thread.currentThread().sleep(100L);;
  } catch (InterruptedException e) {
      // ignore
  }
     }
     transmit(p);

 } catch(Exception e) {
     e.printStackTrace();
     System.exit(1);
 }
    }
}

And here is receiving end, RTPClient:

    package client;


import javax.media.*;

public class RTPClient implements ControllerListener, Runnable {

    Player p;
    MediaLocator src;

    public static void main(String[] args) {
        RTPClient rtp = new RTPClient("192.168.1.100");
        Thread t = new Thread(rtp);
        t.start();

    }

    public RTPClient(String ip) {
 String srcUrl = "rtp://" + ip + ":42050/audio/1";
 DataSink sink;
 src = new MediaLocator(srcUrl);
    }
    public void run() {
        try {
     p = Manager.createPlayer(src);
     p.addControllerListener(this);
     p.start();
 } catch(Exception e) {
     e.printStackTrace();
     System.exit(1);
 }
    }

    public synchronized void controllerUpdate(ControllerEvent evt) {
 if (evt instanceof EndOfMediaEvent) {
     System.exit(0);
 } else {
     System.out.println(evt.toString());
 }
    }
}  

I figured, it successfully sends the whatever file I choose, but when I send .mp3, Client won't play it. I get:

    RTP Handler internal error:
javax.media.ControllerErrorEvent[source=com.sun.media.content.unknown.Handler@9ed927,message=Internal
module com.sun.media.BasicRendererModule@1386000: failed to handle a data
format change!]

Interesting thing is, .wav is sent perfectly. So my guess was is the format set prior to sending. And I tried changing format to some other supported format, but then I get bunch of other errors.

    Failed to build a graph for the given custom options.
Failed to realize: com.sun.media.ProcessEngine@eee36c
  Cannot build a flow graph with the customized options:
    Unable to transcode format: mpegaudio, 48000.0 Hz, 16-bit, Stereo, LittleEndian, Signed, 20000.0 frame rate, FrameSize=11264 bits
      to: ULAW/rtp, 8000.0 Hz, 8-bit, Stereo
      outputting to: RAW/RTP
Error: Unable to realize com.sun.media.ProcessEngine@eee36c

Finally, I opened JMStudio (the built-in app for sending/receiving media streams in JMF), and when I try to stream .mp3, I get exact same error as when running my app. JMF is set up fine, I checked PATH and CLASSPATH, also I installed mp3plugin which is also setup fine. Everything seems fine, but it just doesn't work! At least .mp3 is not. So, how can I make .mp3 "go to the other end"?

3

There are 3 answers

1
Nob Venoda On BEST ANSWER

Solved.

All I had to do is add these lines in constructor for sender/receiver.

Format input1 = new AudioFormat(AudioFormat.MPEGLAYER3);
Format input2 = new AudioFormat(AudioFormat.MPEG);
Format output = new AudioFormat(AudioFormat.LINEAR);
PlugInManager.addPlugIn(
        "com.sun.media.codec.audio.mp3.JavaDecoder",
        new Format[]{input1, input2},
        new Format[]{output},
        PlugInManager.CODEC);

Might help somebody else with this problem :) Still don't know why JMStudio isn't working... Not that I care anymore.

1
hoymkot On

There are a couple things to do to make the code in question works:

  1. put mp3plugin.jar in the classpath. It is a mp3 plugin for JMF. You may find it online.
  2. put the following code in the main method to register the newly added plugin.

    Format input1 = new AudioFormat(AudioFormat.MPEGLAYER3);
    Format input2 = new AudioFormat(AudioFormat.MPEG);
    Format output = new AudioFormat(AudioFormat.LINEAR);
    PlugInManager.addPlugIn(
            "com.sun.media.codec.audio.mp3.JavaDecoder",
            new Format[]{input1, input2},
            new Format[]{output},
            PlugInManager.CODEC);
    
  3. set the track format to AduioFormat.DVI_RTP in the RTPServer.java to convert your mp3 music to a format that RTPClient can play.

Before

if (supported.length > 0) {
      chosen = supported[0]; // this is where I tried changing formats
      tracks[i].setFormat(chosen);
      System.err.println("Track " + i + " is set to transmit as: " +chosen);
      atLeastOneTrack = true;
  } else

After ( replace "chosen" with "new AudioFormat(AudioFormat.DVI_RTP)" )

if (supported.length > 0) {
      chosen = supported[0]; // this is where I tried changing formats
      tracks[i].setFormat(new AudioFormat(AudioFormat.DVI_RTP));
      atLeastOneTrack = true;
  } else

Then everything should work just fine.

Here is my RTPServer

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;    
import javax.media.rtp.*;
import javax.media.rtp.rtcp.*;
import javax.media.*;
import javax.media.protocol.*;
import javax.media.control.*;
import javax.media.format.AudioFormat;

public class RTPServerMP3 implements ControllerListener {
    private String ipAddress;
    Processor p;
    public static void main(String[] args) throws NoProcessorException, IOException {
        Format input1 = new AudioFormat(AudioFormat.MPEGLAYER3);
        Format input2 = new AudioFormat(AudioFormat.MPEG);
        Format output = new AudioFormat(AudioFormat.LINEAR);
        PlugInManager.addPlugIn(
                "com.sun.media.codec.audio.mp3.JavaDecoder",
                new Format[]{input1, input2},
                new Format[]{output},
                PlugInManager.CODEC);
        RTPServerMP3 rtp = new RTPServerMP3("192.168.1.86");
        rtp.p = Manager.createProcessor(new MediaLocator((new File( "roar_of_future.mp3")).toURL()));
        rtp.p.addControllerListener(rtp);
        rtp.p.configure();
    }
    public RTPServerMP3(String ip) throws MalformedURLException {
        ipAddress = ip;
    }
    private void setTrackFormat(Processor p) {
        // Get the tracks from the processor
        TrackControl[] tracks = p.getTrackControls();
        // Do we have atleast one track?
        if (tracks == null || tracks.length < 1) {
            System.out.println("Couldn't find tracks in processor");
            System.exit(1);
        }
        // Set the output content descriptor to RAW_RTP
        // This will limit the supported formats reported from
        // Track.getSupportedFormats to only valid RTP formats.
        ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP);
        p.setContentDescriptor(cd);
        Format supported[];
        Format chosen;
        boolean atLeastOneTrack = false;
        // Program the tracks.
        for (int i = 0; i < tracks.length; i++) {
            Format format = tracks[i].getFormat();
            System.out.println("seeing format " + format.getEncoding() + " for track " + i);
            if (tracks[i].isEnabled()) {
                supported = tracks[i].getSupportedFormats();
                for (int n = 0; n < supported.length; n++)
                    System.out.println("Supported format: " + supported[n]);
                // We've set the output content to the RAW_RTP.
                // So all the supported formats should work with RTP.
                // We'll just pick the first one.
                if (supported.length > 0) {
                    chosen = supported[0]; // this is where I tried changing formats
                    tracks[i].setFormat(new AudioFormat(AudioFormat.DVI_RTP));
                    System.err.println("Track " + i + " is set to transmit as: " + chosen);
                    atLeastOneTrack = true;
                } else
                    tracks[i].setEnabled(false);
            } else
                tracks[i].setEnabled(false);
        }
    }

    private void transmit(Processor p) {
        try {
            DataSource output = p.getDataOutput();
            PushBufferDataSource pbds = (PushBufferDataSource) output;
            RTPManager rtpMgr = RTPManager.newInstance();
            SessionAddress localAddr, destAddr;
            SendStream sendStream;
            int port = 49150;
            SourceDescription srcDesList[];
            localAddr = new SessionAddress(InetAddress.getLocalHost(), port/2+10);
            InetAddress ipAddr = InetAddress.getByName(ipAddress);
            destAddr = new SessionAddress(ipAddr, port);
            rtpMgr.initialize(localAddr);
            rtpMgr.addTarget(destAddr);
            sendStream = rtpMgr.createSendStream(output, 0);
            sendStream.start();
            System.err.println("Created RTP session: " + ipAddress + " " + port);
            p.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public synchronized void controllerUpdate(ControllerEvent evt) {
        if (evt instanceof RealizeCompleteEvent) {
            transmit(p);
        } else if (evt instanceof ConfigureCompleteEvent) {
            setTrackFormat(p);
            p.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW_RTP));
            p.realize();
        } else if (evt instanceof EndOfMediaEvent) {
            System.exit(0);
        } 
    }
}

Here is my RTPClient

import java.io.IOException;
import javax.media.*;

public class RTPClientMP3  {
    public static void main(String[] args) throws NoPlayerException, CannotRealizeException, IOException {
        String srcUrl = "rtp://192.168.1.86:49150/audio/1";
        MediaLocator src = new MediaLocator(srcUrl);
        Player player = Manager.createRealizedPlayer(src);
        player.start();

    }
}
0
hoymkot On

My environment cannot detect the newly added plugin. I would have to hardcode the codec into the track. It works but the mp3 is cluttering. .wav is perfectly fine though.

javax.media.Codec codec = (javax.media.Codec) (Class.forName(plugins.get(0)).newInstance());
    com.sun.media.codec.audio.mp3.JavaDecoder decoder = new com.sun.media.codec.audio.mp3.JavaDecoder();
    Codec[] cc = new Codec[2];
    cc[0] = codec;
    cc[1] = decoder;
    try {
        tracks[0].setCodecChain(cc);
    } catch (UnsupportedPlugInException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NotConfiguredError e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }