【问题标题】:Display RTP MJPEG显示 RTP MJPEG
【发布时间】:2013-05-19 06:49:45
【问题描述】:

我正在寻找一种使用 JavaFx 显示 RTP JPEG 流的解决方案。 我可以显示文件中的 jpeg 并接收 RTP JPEG 流并将其拆分以识别RFC2435 中指定的所有参数和数据 但我不知道如何将我的 JPEG 数组转换为可显示的图像。 我不想自己实现 JPEG 解码器。 有什么想法吗?

【问题讨论】:

    标签: java arrays jpeg rtp


    【解决方案1】:

    利用 JavaFX 的内置 jpeg 解码器,它应该能够解码 Image constructor 中的 jpeg 图像。

    class MJPEGViewer extends ImageView {
      MJPEGViewer() {
        // setup a thread which processes the input stream.
        // the processing thread invokes onNewData for each new frame.
      }
    
      private void onNewData(byte[] jpegData) {
        imageView.set(
          new Image(
            new ByteArrayInputStream(jpegData);
          )
        );
      }
    }
    

    jpegData 是一个字节数组,假定包含从 RTP 流中提取的帧的 JFIF 数据。

    可执行示例

    这是一个 mjpeg 电影播放器​​,可从以下位置播放电影: http://inst.eecs.berkeley.edu/~ee122/sp06/ProgAsgns/movie.Mjpeg

    基于Programming Assignment 5: Streaming Video with RTSP and RTP 的视频流课程(我希望这不是你的家庭作业课程)。

    根据视频流类描述,它是一种“专有的 MJPEG 格式”,因此您需要根据 RFC2435 对符合标准的格式进行自己的解码。

    播放器可以正常工作,但在正确解码 JPEG 时确实存在问题,我还没有调查过。 “专有 MJPEG 格式”示例影片中的 JPEG 未正确编码,或者 JavaFX JPEG 编解码器在解码帧时出错。可见的伪影是您可以看到图像,但图像的颜色不正确(具有粉红色阴影)。这可能是RT-14647 Incorrect display of JPEG images 的一个实例,因为视频中的粉色阴影在错误中显示的解码错误的 JPEG 中看起来相同。您可以在下面的示例代码呈现的视频截图中清楚地看到粉色阴影。该错误仅影响一些 JPEG 图像(我在 JavaFX 中使用的绝大多数 JPEG 图像都显示良好)。因此,您只需要尝试使用您的视频流来查看 JavaFX jpeg 解码器是否为您正确解码 jpeg 图像。

    而不是每次都替换图像视图中的图像,使用WritableImage 并直接更新它的像素缓冲区可能更有效,但是蛮力替换图像方法对我来说似乎工作正常。

    import javafx.animation.*;
    import javafx.application.Application;
    import javafx.event.*;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.image.*;
    import javafx.scene.layout.*;
    import javafx.stage.Stage;
    import javafx.util.Duration;
    
    import java.io.*;
    import java.util.Arrays;
    
    public class MjpegPlayer extends Application {
      public static void main(String[] args) { Application.launch(MjpegPlayer.class); }
    
      // ADJUST THIS LOCATION TO SET THE LOCATION OF YOUR MOVIE FILE!!
      private static final String MOVIE_FILE = "/Users/lilyshard/dev/playfx/src/fruits/movie.Mjpeg";
    
      private VideoStream vs;
    
      @Override public void start(Stage stage) throws Exception {
        vs = new VideoStream(MOVIE_FILE);
    
        final ImageView viewer   = new ImageView();
        final Timeline  timeline = createTimeline(viewer);
    
        VBox layout = new VBox(20);
        layout.setStyle("-fx-background-color: cornsilk;");
        layout.setAlignment(Pos.CENTER);
        layout.getChildren().setAll(
          viewer,
          createControls(timeline)
        );
    
        stage.setScene(new Scene(layout, 400, 400));
        stage.show();
    
        timeline.play();
      }
    
      private Timeline createTimeline(final ImageView viewer) {
        final Timeline timeline = new Timeline();
        final byte[] buf = new byte[15000];
    
        timeline.getKeyFrames().setAll(
          new KeyFrame(Duration.ZERO, new EventHandler<ActionEvent>() {
            @Override public void handle(ActionEvent event) {
              try {
                int len = vs.getnextframe(buf);
                if (len == -1) {
                  timeline.stop();
                  return;
                }
                viewer.setImage(
                  new Image(
                    new ByteArrayInputStream(
                      Arrays.copyOf(buf, len)
                    )
                  )
                );
              } catch (Exception e) {
                e.printStackTrace();
              }
            }
          }),
          new KeyFrame(Duration.seconds(1.0/24))
        );
        timeline.setCycleCount(Timeline.INDEFINITE);
    
        return timeline;
      }
    
      private HBox createControls(final Timeline timeline) {
        Button play = new Button("Play");
        play.setOnAction(new EventHandler<ActionEvent>() {
          @Override
          public void handle(ActionEvent event) {
            timeline.play();
          }
        });
    
        Button pause = new Button("Pause");
        pause.setOnAction(new EventHandler<ActionEvent>() {
          @Override
          public void handle(ActionEvent event) {
            timeline.pause();
          }
        });
    
        Button restart = new Button("Restart");
        restart.setOnAction(new EventHandler<ActionEvent>() {
          @Override
          public void handle(ActionEvent event) {
            try {
              timeline.stop();
              vs = new VideoStream(MOVIE_FILE);
              timeline.playFromStart();
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        });
    
        HBox controls = new HBox(10);
        controls.setAlignment(Pos.CENTER);
        controls.getChildren().setAll(
          play,
          pause,
          restart
        );
        return controls;
      }
    }
    
    class VideoStream {
    
      FileInputStream fis; //video file
      int frame_nb; //current frame nb
    
      public VideoStream(String filename) throws Exception{
    
        //init variables
        fis = new FileInputStream(filename);
        frame_nb = 0;
      }
    
      public int getnextframe(byte[] frame) throws Exception
      {
        int length = 0;
        String length_string;
        byte[] frame_length = new byte[5];
    
        //read current frame length
        fis.read(frame_length,0,5);
    
        //transform frame_length to integer
        length_string = new String(frame_length);
        try {
          length = Integer.parseInt(length_string);
        } catch (Exception e) {
          return -1;
        }
    
        return(fis.read(frame,0,length));
      }
    }
    

    更新

    我尝试在 Windows 7 上使用 Java 8u20 早期访问版本 11 再次运行该程序,并且视频播放良好,没有任何粉红色调,所以我猜导致该问题的任何原因现在已在以后的 Java 版本中得到修复.

    【讨论】:

    • 更新帖子以包含数据类型和示例实现。
    • @jewelsea 无论如何我可以从网络摄像机流式传输 mjpeg 广播吗?我尝试使用 HttpURLConnection 和 DataInputStream 而不是 FileInputStream 但我认为它不是这样工作的。请问有什么建议吗?
    • @MinNaingOo 在这里回答这个问题有点太难了。此答案中提供的代码的改编可能会帮助您获得解决方案(或者可能不会)。我猜你只需要用你的输入流数据来试试看。如果它对您不起作用,那么也许可以在 Google 上搜索“mjpeg java”(包括基于 swing 的解决方案),看看是否有第三方库可以帮助您。
    猜你喜欢
    • 2019-06-26
    • 2014-04-16
    • 2021-04-13
    • 2015-08-22
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    相关资源
    最近更新 更多