使用java套接字在网络上进行视频流传输

时间:2015-01-19 11:14:38

标签: java sockets streaming server rtsp

我正在开发一个网络java应用程序。我想从网络流式传输视频(可能使用套接字)。我在互联网上搜索但我找不到任何有效的服务器和客户端代码来将视频从服务器流式传输到客户端。 任何人都可以找到流媒体服务器和客户端或编写一个简单的程序,以便我可以理解如何使用java完成流式传输。

PS。我在互联网上安排了与此相关的任务。但它有错误,一些方法也没有实现。如果您可以删除错误并完成方法,它也会有所帮助.. http://cs.anu.edu.au/student/comp3310/2004/Labs/lab6/lab5.html

4 个答案:

答案 0 :(得分:2)

以下是基本代码:http://xuggle.googlecode.com/svn/trunk/java/xuggle-xuggler/src/com/xuggle/xuggler/demos/DecodeAndPlayAudioAndVideo.java

但我改为:

package Pasban;

/**
 *
 * @modified by Pasban
 */
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

import com.xuggle.xuggler.Global;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IPixelFormat;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.Utils;
import com.xuggle.xuggler.demos.VideoImage;
import java.awt.Dimension;

/**
 * Takes a media container, finds the first video stream,
 * decodes that stream, and then plays the audio and video.
 *
 * This code does a VERY coarse job of matching time-stamps, and thus
 * the audio and video will float in and out of slight sync.  Getting
 * time-stamps syncing-up with audio is very system dependent and left
 * as an exercise for the reader.
 * 
 * @author aclarke
 *
 */
public class DecodeAndPlayAudioAndVideo {

    /**
     * The audio line we'll output sound to; it'll be the default audio device on your system if available
     */
    private static SourceDataLine mLine;
    /**
     * The window we'll draw the video on.
     * 
     */
    private static VideoImage mScreen = null;
    private static long mSystemVideoClockStartTime;
    private static long mFirstVideoTimestampInStream;

    /**
     * Takes a media container (file) as the first argument, opens it,
     * plays audio as quickly as it can, and opens up a Swing window and displays
     * video frames with <i>roughly</i> the right timing.
     *  
     * @param args Must contain one string which represents a filename
     */
    @SuppressWarnings("deprecation")
    public static void main(String[] args) {

        String filename = "http://techslides.com/demos/sample-videos/small.mp4";

        // Let's make sure that we can actually convert video pixel formats.
        if (!IVideoResampler.isSupported(IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION)) {
            throw new RuntimeException("you must install the GPL version of Xuggler (with IVideoResampler support) for this demo to work");
        }

        // Create a Xuggler container object
        IContainer container = IContainer.make();

        // Open up the container
        if (container.open("http://techslides.com/demos/sample-videos/small.mp4", IContainer.Type.READ, null) < 0) {
            throw new IllegalArgumentException("could not open file: " + filename);
        }

        // query how many streams the call to open found
        int numStreams = container.getNumStreams();

        // and iterate through the streams to find the first audio stream
        int videoStreamId = -1;
        IStreamCoder videoCoder = null;
        int audioStreamId = -1;
        IStreamCoder audioCoder = null;
        for (int i = 0; i < numStreams; i++) {
            // Find the stream object
            IStream stream = container.getStream(i);
            // Get the pre-configured decoder that can decode this stream;
            IStreamCoder coder = stream.getStreamCoder();

            if (videoStreamId == -1 && coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
                videoStreamId = i;
                videoCoder = coder;
            } else if (audioStreamId == -1 && coder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO) {
                audioStreamId = i;
                audioCoder = coder;
            }
        }
        if (videoStreamId == -1 && audioStreamId == -1) {
            throw new RuntimeException("could not find audio or video stream in container: " + filename);
        }

        /*
         * Check if we have a video stream in this file.  If so let's open up our decoder so it can
         * do work.
         */
        IVideoResampler resampler = null;
        if (videoCoder != null) {
            if (videoCoder.open() < 0) {
                throw new RuntimeException("could not open audio decoder for container: " + filename);
            }

            if (videoCoder.getPixelType() != IPixelFormat.Type.BGR24) {
                // if this stream is not in BGR24, we're going to need to
                // convert it.  The VideoResampler does that for us.
                resampler = IVideoResampler.make(videoCoder.getWidth(), videoCoder.getHeight(), IPixelFormat.Type.BGR24,
                        videoCoder.getWidth(), videoCoder.getHeight(), videoCoder.getPixelType());

                openJavaVideo(videoCoder);


                if (resampler == null) {
                    throw new RuntimeException("could not create color space resampler for: " + filename);
                }
            }
            /*
             * And once we have that, we draw a window on screen
             */
        }

        if (audioCoder != null) {
            if (audioCoder.open() < 0) {
                throw new RuntimeException("could not open audio decoder for container: " + filename);
            }

            /*
             * And once we have that, we ask the Java Sound System to get itself ready.
             */
            try {
                openJavaSound(audioCoder);
            } catch (LineUnavailableException ex) {
                throw new RuntimeException("unable to open sound device on your system when playing back container: " + filename);
            }
        }


        /*
         * Now, we start walking through the container looking at each packet.
         */
        IPacket packet = IPacket.make();
        mFirstVideoTimestampInStream = Global.NO_PTS;
        mSystemVideoClockStartTime = 0;
        while (container.readNextPacket(packet) >= 0) {
            /*
             * Now we have a packet, let's see if it belongs to our video stream
             */
            if (packet.getStreamIndex() == videoStreamId) {
                /*
                 * We allocate a new picture to get the data out of Xuggler
                 */
                IVideoPicture picture = IVideoPicture.make(videoCoder.getPixelType(),
                        videoCoder.getWidth(), videoCoder.getHeight());

                /*
                 * Now, we decode the video, checking for any errors.
                 * 
                 */
                int bytesDecoded = videoCoder.decodeVideo(picture, packet, 0);
                if (bytesDecoded < 0) {
                    throw new RuntimeException("got error decoding audio in: " + filename);
                }

                /*
                 * Some decoders will consume data in a packet, but will not be able to construct
                 * a full video picture yet.  Therefore you should always check if you
                 * got a complete picture from the decoder
                 */
                if (picture.isComplete()) {
                    IVideoPicture newPic = picture;
                    /*
                     * If the resampler is not null, that means we didn't get the video in BGR24 format and
                     * need to convert it into BGR24 format.
                     */
                    if (resampler != null) {
                        // we must resample
                        newPic = IVideoPicture.make(resampler.getOutputPixelFormat(), picture.getWidth(), picture.getHeight());
                        if (resampler.resample(newPic, picture) < 0) {
                            throw new RuntimeException("could not resample video from: " + filename);
                        }
                    }
                    if (newPic.getPixelType() != IPixelFormat.Type.BGR24) {
                        throw new RuntimeException("could not decode video as BGR 24 bit data in: " + filename);
                    }

                    long delay = millisecondsUntilTimeToDisplay(newPic);
                    // if there is no audio stream; go ahead and hold up the main thread.  We'll end
                    // up caching fewer video pictures in memory that way.
                    try {
                        if (delay > 0) {
                            Thread.sleep(delay);
                        }
                    } catch (InterruptedException e) {
                        return;
                    }

                    // And finally, convert the picture to an image and display it

                    mScreen.setImage(Utils.videoPictureToImage(newPic));
                }
            } else if (packet.getStreamIndex() == audioStreamId) {
                /*
                 * We allocate a set of samples with the same number of channels as the
                 * coder tells us is in this buffer.
                 * 
                 * We also pass in a buffer size (1024 in our example), although Xuggler
                 * will probably allocate more space than just the 1024 (it's not important why).
                 */
                IAudioSamples samples = IAudioSamples.make(1024, audioCoder.getChannels());

                /*
                 * A packet can actually contain multiple sets of samples (or frames of samples
                 * in audio-decoding speak).  So, we may need to call decode audio multiple
                 * times at different offsets in the packet's data.  We capture that here.
                 */
                int offset = 0;

                /*
                 * Keep going until we've processed all data
                 */
                while (offset < packet.getSize()) {
                    int bytesDecoded = audioCoder.decodeAudio(samples, packet, offset);
                    if (bytesDecoded < 0) {
                        throw new RuntimeException("got error decoding audio in: " + filename);
                    }
                    offset += bytesDecoded;
                    /*
                     * Some decoder will consume data in a packet, but will not be able to construct
                     * a full set of samples yet.  Therefore you should always check if you
                     * got a complete set of samples from the decoder
                     */
                    if (samples.isComplete()) {
                        // note: this call will block if Java's sound buffers fill up, and we're
                        // okay with that.  That's why we have the video "sleeping" occur
                        // on another thread.
                        playJavaSound(samples);
                    }
                }
            } else {
                /*
                 * This packet isn't part of our video stream, so we just silently drop it.
                 */
                do {
                } while (false);
            }

        }
        /*
         * Technically since we're exiting anyway, these will be cleaned up by 
         * the garbage collector... but because we're nice people and want
         * to be invited places for Christmas, we're going to show how to clean up.
         */
        if (videoCoder != null) {
            videoCoder.close();
            videoCoder = null;
        }
        if (audioCoder != null) {
            audioCoder.close();
            audioCoder = null;
        }
        if (container != null) {
            container.close();
            container = null;
        }
        closeJavaSound();
        closeJavaVideo();
    }

    private static long millisecondsUntilTimeToDisplay(IVideoPicture picture) {
        /**
         * We could just display the images as quickly as we decode them, but it turns
         * out we can decode a lot faster than you think.
         * 
         * So instead, the following code does a poor-man's version of trying to
         * match up the frame-rate requested for each IVideoPicture with the system
         * clock time on your computer.
         * 
         * Remember that all Xuggler IAudioSamples and IVideoPicture objects always
         * give timestamps in Microseconds, relative to the first decoded item.  If
         * instead you used the packet timestamps, they can be in different units depending
         * on your IContainer, and IStream and things can get hairy quickly.
         */
        long millisecondsToSleep = 0;
        if (mFirstVideoTimestampInStream == Global.NO_PTS) {
            // This is our first time through
            mFirstVideoTimestampInStream = picture.getTimeStamp();
            // get the starting clock time so we can hold up frames
            // until the right time.
            mSystemVideoClockStartTime = System.currentTimeMillis();
            millisecondsToSleep = 0;
        } else {
            long systemClockCurrentTime = System.currentTimeMillis();
            long millisecondsClockTimeSinceStartofVideo = systemClockCurrentTime - mSystemVideoClockStartTime;
            // compute how long for this frame since the first frame in the stream.
            // remember that IVideoPicture and IAudioSamples timestamps are always in MICROSECONDS,
            // so we divide by 1000 to get milliseconds.
            long millisecondsStreamTimeSinceStartOfVideo = (picture.getTimeStamp() - mFirstVideoTimestampInStream) / 1000;
            final long millisecondsTolerance = 50; // and we give ourselfs 50 ms of tolerance
            millisecondsToSleep = (millisecondsStreamTimeSinceStartOfVideo
                    - (millisecondsClockTimeSinceStartofVideo + millisecondsTolerance));
        }
        return millisecondsToSleep;
    }

    /**
     * Opens a Swing window on screen.
     */
    /**
     * Forces the swing thread to terminate; I'm sure there is a right
     * way to do this in swing, but this works too.
     */
    private static void closeJavaVideo() {
        System.exit(0);
    }

    private static void openJavaSound(IStreamCoder aAudioCoder) throws LineUnavailableException {
        AudioFormat audioFormat = new AudioFormat(aAudioCoder.getSampleRate(),
                (int) IAudioSamples.findSampleBitDepth(aAudioCoder.getSampleFormat()),
                aAudioCoder.getChannels(),
                true, /* xuggler defaults to signed 16 bit samples */
                false);
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
        mLine = (SourceDataLine) AudioSystem.getLine(info);
        /**
         * if that succeeded, try opening the line.
         */
        mLine.open(audioFormat);
        /**
         * And if that succeed, start the line.
         */
        mLine.start();


    }

    private static void playJavaSound(IAudioSamples aSamples) {
        /**
         * We're just going to dump all the samples into the line.
         */
        byte[] rawBytes = aSamples.getData().getByteArray(0, aSamples.getSize());
        mLine.write(rawBytes, 0, aSamples.getSize());
    }

    private static void closeJavaSound() {
        if (mLine != null) {
            /*
             * Wait for the line to finish playing
             */
            mLine.drain();
            /*
             * Close the line.
             */
            mLine.close();
            mLine = null;
        }
    }

    private static void openJavaVideo(IStreamCoder videoCoder) {
        mScreen = new VideoImage();
        mScreen.setPreferredSize(new Dimension(videoCoder.getWidth(), videoCoder.getHeight()));
        mScreen.setLocationRelativeTo(null);
    }
}

我改变的事情:

private static void openJavaVideo(IStreamCoder videoCoder) {
        mScreen = new VideoImage();
        mScreen.setPreferredSize(new Dimension(videoCoder.getWidth(), videoCoder.getHeight()));
        mScreen.setLocationRelativeTo(null);
    }

openJavaVideo 方法移入videoStream探测器:

openJavaVideo(videoCoder);

更改了主要内容的第一部分:

public static void main(String[] args) {

        String filename = "http://techslides.com/demos/sample-videos/small.mp4";

        // Let's make sure that we can actually convert video pixel formats.
        if (!IVideoResampler.isSupported(IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION)) {
            throw new RuntimeException("you must install the GPL version of Xuggler (with IVideoResampler support) for this demo to work");
        }

        // Create a Xuggler container object
        IContainer container = IContainer.make();

        // Open up the container
        if (container.open("http://techslides.com/demos/sample-videos/small.mp4", IContainer.Type.READ, null) < 0) {
            throw new IllegalArgumentException("could not open file: " + filename);
        }

实际上,重要的部分是:

if (container.open("http://techslides.com/demos/sample-videos/small.mp4", IContainer.Type.READ, null) < 0) {
            throw new IllegalArgumentException("could not open file: " + filename);
        }

答案 1 :(得分:1)

请参阅:Any simple (and up to date) Java frameworks for embedding movies within a Swing Application?,只需参考JavaFX代码示例(您不需要任何Swing代码)。

import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.media.*;
import javafx.stage.Stage;

public class VideoPlayerExample extends Application {
  public static void main(String[] args) throws Exception { launch(args); }
  @Override public void start(final Stage stage) throws Exception {
    final MediaPlayer oracleVid = new MediaPlayer(
      new Media("http://download.oracle.com/otndocs/products/javafx/oow2010-2.flv")
    );
    stage.setScene(new Scene(new Group(new MediaView(oracleVid)), 540, 208));
    stage.show();

    oracleVid.play();
  }
}

因此,将您的视频编码为format understood by JavaFX(例如h264编码的mp4)并将其放在http服务器上,您可以通过http从JavaFX客户端加载视频数据。确保您的客户是certified system configuration for media playback using JavaFX

这可能足以满足您的需求。


如果您需要更多花哨的东西,JavaFX还支持http live streaming,您可以阅读并查看是否需要(您可能不会这样做)。我没有关于设置http直播流媒体服务器的说明,也没有关于如何做到这一点的互联网上某个地方的链接(如果你想走这条路,你必须自己做研究)。


另外,请注意,我将mjpeg player lab assignment you reference in your question转换为JavaFX以回答问题:Display RTP MJPEG。如果您想要在较低级别了解如何完成此类视频播放,这将非常有用。但是,我不建议将此方法用于生产项目的视频播放 - 而只是使用内置的JavaFX MediaPlayer。

答案 2 :(得分:0)

Xuggle是最好的之一:

Streaming video with Xuggler

现在我没有;我有一个完整的项目,但我相信它有一个示例及其演示文件。

使用Xuggler搜索谷歌的xuggle视频流演示或类似关键字。它易于使用并支持大多数已知格式,因为它自己包装了FFMPEG。

答案 3 :(得分:0)

我想到了另一个可能值得尝试的想法。

  1. 创建JavaFX应用程序。
  2. 在其中添加网络浏览器,检查 webengine
  3. 创建一个包含html播放器的模板网页,或者在服务器中加载接受文件ID的页面,然后创建一个为该文件创建播放器的页面,类似于youtube,然后自动播放。
  4. 你可以做到这一点会更好。

    有webengine和javaFX的示例代码。一旦你加载了一个页面,比如youtube或vimeo并在那里播放了一个视频,那么天空是极限:)