Skip to content

Instantly share code, notes, and snippets.

@giacobenin
Created August 5, 2016 20:12
Show Gist options
  • Save giacobenin/7715e57cc0c2fbbdc2a1ba5dbf3b8535 to your computer and use it in GitHub Desktop.
Save giacobenin/7715e57cc0c2fbbdc2a1ba5dbf3b8535 to your computer and use it in GitHub Desktop.
Media Streaming using VLCJ
package us.ihmc.netcacher.net;
import java.io.File;
import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.co.caprica.vlcj.binding.internal.libvlc_media_t;
import uk.co.caprica.vlcj.player.MediaPlayer;
import uk.co.caprica.vlcj.player.MediaPlayerEventListener;
import uk.co.caprica.vlcj.player.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.MediaPlayerLatch;
import uk.co.caprica.vlcj.player.headless.HeadlessMediaPlayer;
/**
*
* @author Giacomo Benincasa (gbenincasa@ihmc.us)
*/
public class VideoStreamer implements Runnable {
abstract class MediaPlayerFinishedEventListener implements MediaPlayerEventListener{
@Override
public void mediaChanged(MediaPlayer mp, libvlc_media_t l, String string) {}
@Override
public void opening(MediaPlayer mp) {}
@Override
public void buffering(MediaPlayer mp, float f) {}
@Override
public void playing(MediaPlayer mp) {}
@Override
public void paused(MediaPlayer mp) {}
@Override
public void stopped(MediaPlayer mp) {}
@Override
public void forward(MediaPlayer mp) {}
@Override
public void backward(MediaPlayer mp) {}
@Override
public void timeChanged(MediaPlayer mp, long l) {}
@Override
public void positionChanged(MediaPlayer mp, float f) {}
@Override
public void seekableChanged(MediaPlayer mp, int i) {}
@Override
public void pausableChanged(MediaPlayer mp, int i) {}
@Override
public void titleChanged(MediaPlayer mp, int i) {}
@Override
public void snapshotTaken(MediaPlayer mp, String string) {}
@Override
public void lengthChanged(MediaPlayer mp, long l) {}
@Override
public void videoOutput(MediaPlayer mp, int i) {}
@Override
public void scrambledChanged(MediaPlayer mp, int i) {}
@Override
public void elementaryStreamAdded(MediaPlayer mp, int i, int i1) {}
@Override
public void elementaryStreamDeleted(MediaPlayer mp, int i, int i1) {}
@Override
public void elementaryStreamSelected(MediaPlayer mp, int i, int i1) {}
@Override
public void corked(MediaPlayer mp, boolean bln) {}
@Override
public void muted(MediaPlayer mp, boolean bln) {}
@Override
public void volumeChanged(MediaPlayer mp, float f) {}
@Override
public void audioDeviceChanged(MediaPlayer mp, String string) {}
@Override
public void chapterChanged(MediaPlayer mp, int i) {}
@Override
public void error(MediaPlayer mp) {}
@Override
public void mediaMetaChanged(MediaPlayer mp, int i) {}
@Override
public void mediaSubItemAdded(MediaPlayer mp, libvlc_media_t l) {}
@Override
public void mediaDurationChanged(MediaPlayer mp, long l) {}
@Override
public void mediaParsedChanged(MediaPlayer mp, int i) {}
@Override
public void mediaFreed(MediaPlayer mp) {}
@Override
public void mediaStateChanged(MediaPlayer mp, int i) {}
@Override
public void mediaSubItemTreeAdded(MediaPlayer mp, libvlc_media_t l) {}
@Override
public void newMedia(MediaPlayer mp) {}
@Override
public void subItemPlayed(MediaPlayer mp, int i) {}
@Override
public void subItemFinished(MediaPlayer mp, int i) {}
@Override
public void endOfSubItems(MediaPlayer mp) {}
}
private final Logger logger = LoggerFactory.getLogger(VideoStreamer.class);
private final Collection<VideoStreamerListener> listeners = new LinkedList<>();
private final File resource;
private final Protocol protocol;
private final String id;
private String streamingAddr;
private int streamingPort;
public enum Protocol {
HTTP,
RTSP,
RTP
}
public VideoStreamer(File resource, String id) {
this(resource, id, Protocol.RTSP);
}
public VideoStreamer(File resource, String id, Protocol protocol) {
this(resource, id, protocol, "127.0.0.1", 5555);
}
public VideoStreamer(File resource, String id, String streamingAddr, int streamingPort) {
this(resource, id, Protocol.RTSP, "127.0.0.1", 5555);
}
public VideoStreamer(File resource, String id, Protocol protocol, String streamingAddr, int streamingPort) {
this.resource = resource;
this.protocol = protocol;
this.streamingAddr = streamingAddr;
this.streamingPort = streamingPort;
this.id = id;
}
public void addListener(VideoStreamerListener listener) {
listeners.add(listener);
}
public String getStreamURL() {
return buildURL(protocol, streamingAddr, streamingPort, id);
}
//when running this it requires an MRL (Media Resource Locator)
//fancy term for saying the file you want to stream. This could be a url to another
//location that streams media or a filepath to a media file you want to stream
//on the system you are running this code on.
@Override
public void run() {
try {
// this is the IP address and port you are wanting to stream at this means
// clients will connect to http://127.0.0.1:5555 to watch the stream
String options = null;
switch (protocol) {
case HTTP: {
options = formatHttpStream(streamingAddr, streamingPort);
break;
}
case RTP: {
options = formatRtpStream(streamingAddr, streamingPort);
break;
}
case RTSP:
default: {
options = formatRtspStream(streamingAddr, streamingPort, id);
}
}
final String media = resource.getPath();
logger.info("Streaming '" + media + "' to '" + options + "'");
// create headless media player that calls vlc native libraries
MediaPlayerFactory playerfactory = new MediaPlayerFactory(media);
HeadlessMediaPlayer player = playerfactory.newHeadlessMediaPlayer();
final AtomicBoolean videoFinished = new AtomicBoolean(false);
final VideoStreamer lockObj = this;
player.addMediaPlayerEventListener(new MediaPlayerFinishedEventListener(){
@Override
public void finished(MediaPlayer mp) {
logger.info("video " + id + " ended");
videoFinished.set(true);
synchronized(lockObj){
lockObj.notifyAll();
}
}
});
// start the stream
switch (protocol) {
case HTTP: {
player.prepareMedia(media, options);
break;
}
default: {
player.prepareMedia(media, options,
":no-sout-rtp-sap",
":no-sout-standard-sap",
":sout-all",
":sout-keep");
}
}
videoFinished.set(!new MediaPlayerLatch(player).play());
synchronized(lockObj){
while (!videoFinished.get()) {
// Ending the thread kills the player, so don't do it until the
// video has finished
try { lockObj.wait(); }
catch (InterruptedException ex) {}
}
}
try {
player.release();
}
catch(Exception ex){}
}
catch (Exception ex) {
logger.error(ex.getMessage());
}
finally {
logger.info("Streaming for '" + id + "' terminated");
listeners.stream().forEach((listener) -> {
listener.streamEnded(id);
});
}
}
private static String buildURL(Protocol protocol, String serverAddress, int serverPort, String id) {
StringBuilder sb = new StringBuilder(protocol.toString().toLowerCase()).append("://");
if (protocol.equals(Protocol.RTSP)) {
sb.append('@');
}
sb.append(serverAddress).append(":").append(serverPort);
if (protocol.equals(Protocol.RTSP)) {
sb.append("/").append(id);
}
return sb.toString();
}
private static String formatHttpStream(String serverAddress, int serverPort) {
StringBuilder sb = new StringBuilder(60);
sb.append(":sout=#duplicate{dst=std{access=http,mux=ts,");
sb.append("dst=");
sb.append(serverAddress);
sb.append(':');
sb.append(serverPort);
sb.append("}}");
return sb.toString();
}
private static String formatRtspStream(String serverAddress, int serverPort, String id) {
StringBuilder sb = new StringBuilder(60);
sb.append(":sout=#rtp{sdp=rtsp://@");
sb.append(serverAddress);
sb.append(':');
sb.append(serverPort);
sb.append('/');
sb.append(id);
sb.append("}");
return sb.toString();
}
private static String formatRtpStream(String serverAddress, int serverPort) {
StringBuilder sb = new StringBuilder(60);
sb.append(":sout=#rtp{dst=");
sb.append(serverAddress);
sb.append(",port=");
sb.append(serverPort);
sb.append(",mux=ts}");
return sb.toString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment