17

I'm trying to find ways to stream a live video generated in a Java application. The application needs to take screenshots of itself and encode these into a video stream and publish the stream.

So far I have been using Xuggler (a Java library on top of FFMPEG) to encode the screenshots into a video file. This works great. Xuggler claims to be able to transmit live video via RTMP but I have not found any documentation on how to do this programmatically.

  1. Does anyone know how to stream RTMP video programmatically from Xuggler?
  2. Does anyone have a suggestion on other libraries I could use to achieve the same results? I'd prefer to stream the video in MPEG2 over RTP.

I did find someone else asking a very similar question on the Xuggler forums here with no response.

I have looked into JMF and it is not an option for other reasons.

Tansir1
  • 1,789
  • 2
  • 15
  • 28

1 Answers1

24

Honestly don't waste your time with JMF, you can consider that offering dead. Here is how you would do screen shotting to an rtmp stream using h.264 (thanks to tyreus@gmail.com for the example). If the code doesn't show up here's pastebin for it: http://pastebin.com/sJHwj0nW

import com.xuggle.xuggler.Configuration;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IContainerFormat;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IPixelFormat;
import com.xuggle.xuggler.IRational;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.video.ConverterFactory;
import com.xuggle.xuggler.video.IConverter;
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class XugglerRtmpReferenceImpl {

   private static String url = "rtmp://your.test.server/screen/";
   private static String fileName = "test/teststream";
   private static int framesToEncode = 60;
   private static int x = 0;
   private static int y = 0;
   private static int height = 480;
   private static int width = 640;

   public static void main(String[] args) {
       IContainer container = IContainer.make();
       IContainerFormat containerFormat_live = IContainerFormat.make();
       containerFormat_live.setOutputFormat("flv", url + fileName, null);
       container.setInputBufferLength(0);
       int retVal = container.open(url + fileName, IContainer.Type.WRITE, containerFormat_live);
       if (retVal < 0) {
           System.err.println("Could not open output container for live stream");
           System.exit(1);
       }
       IStream stream = container.addNewStream(0);
       IStreamCoder coder = stream.getStreamCoder();
       ICodec codec = ICodec.findEncodingCodec(ICodec.ID.CODEC_ID_H264);
       coder.setNumPicturesInGroupOfPictures(5);
       coder.setCodec(codec);
       coder.setBitRate(200000);
       coder.setPixelType(IPixelFormat.Type.YUV420P);
       coder.setHeight(height);
       coder.setWidth(width);
       System.out.println("[ENCODER] video size is " + width + "x" + height);
       coder.setFlag(IStreamCoder.Flags.FLAG_QSCALE, true);
       coder.setGlobalQuality(0);
       IRational frameRate = IRational.make(5, 1);
       coder.setFrameRate(frameRate);
       coder.setTimeBase(IRational.make(frameRate.getDenominator(), frameRate.getNumerator()));
       Properties props = new Properties();
       InputStream is = XugglerRtmpReferenceImpl.class.getResourceAsStream("/libx264-normal.ffpreset");
       try {
           props.load(is);
       } catch (IOException e) {
           System.err.println("You need the libx264-normal.ffpreset file from the Xuggle distribution in your classpath.");
           System.exit(1);
       }
       Configuration.configure(props, coder);
       coder.open();
       container.writeHeader();
       long firstTimeStamp = System.currentTimeMillis();
       long lastTimeStamp = -1;
       int i = 0;
       try {
           Robot robot = new Robot();
           while (i < framesToEncode) {
               //long iterationStartTime = System.currentTimeMillis();
               long now = System.currentTimeMillis();
               //grab the screenshot
               BufferedImage image = robot.createScreenCapture(new Rectangle(x, y, width, height));
               //convert it for Xuggler
               BufferedImage currentScreenshot = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
               currentScreenshot.getGraphics().drawImage(image, 0, 0, null);
               //start the encoding process
               IPacket packet = IPacket.make();
               IConverter converter = ConverterFactory.createConverter(currentScreenshot, IPixelFormat.Type.YUV420P);
               long timeStamp = (now - firstTimeStamp) * 1000; 
               IVideoPicture outFrame = converter.toPicture(currentScreenshot, timeStamp);
               if (i == 0) {
                   //make first frame keyframe
                   outFrame.setKeyFrame(true);
               }
               outFrame.setQuality(0);
               coder.encodeVideo(packet, outFrame, 0);
               outFrame.delete();
               if (packet.isComplete()) {
                   container.writePacket(packet);
                   System.out.println("[ENCODER] writing packet of size " + packet.getSize() + " for elapsed time " + ((timeStamp - lastTimeStamp) / 1000));
                   lastTimeStamp = timeStamp;
               }
               System.out.println("[ENCODER] encoded image " + i + " in " + (System.currentTimeMillis() - now));
               i++;
               try {
                   Thread.sleep(Math.max((long) (1000 / frameRate.getDouble()) - (System.currentTimeMillis() - now), 0));
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       } catch (AWTException e) {
           e.printStackTrace();
       }
       container.writeTrailer();
    }
}
TheLQ
  • 14,830
  • 14
  • 69
  • 107
Paul Gregoire
  • 9,715
  • 11
  • 67
  • 131
  • Is xuggler stream video from camera and mic in live – Prasanna Anbazhagan Feb 22 '15 at 04:48
  • Yes, xuggler does allow you to stream from a camera and mic, but its not currently the best option when using Java. – Paul Gregoire Feb 22 '15 at 15:06
  • @Mondain Can you please see my questions http://stackoverflow.com/questions/30449366/how-to-send-video-stream-for-live-event-using-youtube-broadcast-in-java & http://stackoverflow.com/questions/30490599/could-not-open-output-container-for-live-stream-using-xuggler-youtube-broadcas – VVB May 30 '15 at 13:53
  • On running this code I get exception this one : http://stackoverflow.com/questions/30489734/could-not-open-output-container-for-live-stream – VVB May 30 '15 at 13:59
  • @PaulGregoire how can I add audio in addition to video in above – kuhajeyan Jun 17 '16 at 21:00
  • 1
    @kuhajeyan I can't say how to add audio here for a couple reasons; 1. I dont use Xuggler anymore and havent for awhile and 2. because its not a trivial thing to do, but it is possible. – Paul Gregoire Jun 17 '16 at 22:59
  • @PaulGregoire Thanks. What is the best alternative to xuggler, and which one would you recommend (in java) – kuhajeyan Jun 18 '16 at 16:01
  • 1
    I'd suggest looking into FMJ or starting here https://github.com/sarxos/webcam-capture – Paul Gregoire Jun 18 '16 at 16:42
  • @PaulGregoire thanks Paul. Btw is red5 moving away from xuggler? – kuhajeyan Jun 19 '16 at 15:34
  • Xuggler was/is independent of Red5 – Paul Gregoire Jun 19 '16 at 23:43
  • 1
    @kuhajeyan I added audio to this example: https://pastebin.com/p5PXKbkm – zholmes1 Apr 17 '17 at 13:53