【发布时间】:2013-05-19 06:49:45
【问题描述】:
我正在寻找一种使用 JavaFx 显示 RTP JPEG 流的解决方案。 我可以显示文件中的 jpeg 并接收 RTP JPEG 流并将其拆分以识别RFC2435 中指定的所有参数和数据 但我不知道如何将我的 JPEG 数组转换为可显示的图像。 我不想自己实现 JPEG 解码器。 有什么想法吗?
【问题讨论】:
我正在寻找一种使用 JavaFx 显示 RTP JPEG 流的解决方案。 我可以显示文件中的 jpeg 并接收 RTP JPEG 流并将其拆分以识别RFC2435 中指定的所有参数和数据 但我不知道如何将我的 JPEG 数组转换为可显示的图像。 我不想自己实现 JPEG 解码器。 有什么想法吗?
【问题讨论】:
利用 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 版本中得到修复.
【讨论】: