Created
August 5, 2016 20:12
-
-
Save giacobenin/7715e57cc0c2fbbdc2a1ba5dbf3b8535 to your computer and use it in GitHub Desktop.
Media Streaming using VLCJ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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