我想使用 MediaPlayer/MediaView 在我的 JavaFX 应用程序中显示网络摄像头的实时流。我的尝试是使用 ffmpeg 录制 HLS 并播放生成的 m3u8 文件,但这会引发以下异常(VLC 播放视频没有问题):
MediaException: UNKNOWN : com.sun.media.jfxmedia.MediaException: Could not create player! : com.sun.media.jfxmedia.MediaException: Could not create player!
at javafx.scene.media.MediaException.exceptionToMediaException(MediaException.java:146)
at javafx.scene.media.MediaPlayer.init(MediaPlayer.java:511)
at javafx.scene.media.MediaPlayer.<init>(MediaPlayer.java:414)
at de.fraunhofer.iosb.ias.flow.assessment.management.monitor.MonitorViewController.testStream(MonitorViewController.java:203)
... 58 more
Caused by: com.sun.media.jfxmedia.MediaException: Could not create player!
at com.sun.media.jfxmediaimpl.NativeMediaManager.getPlayer(NativeMediaManager.java:274)
at com.sun.media.jfxmedia.MediaManager.getPlayer(MediaManager.java:118)
at javafx.scene.media.MediaPlayer.init(MediaPlayer.java:467)
... 60 more
我调试了播放器的创建,当
GSTMediaPlayer.gstInitPlayer()
时在 GSTMediaPlayer 的构造函数中发生错误叫做。此 native 方法返回错误代码 257
, javafx 映射到 MediaError.ERROR_MEDIA_NULL
.我使用以下 ffmpeg 命令录制视频:
ffmpeg -hide_banner -y -rtbufsize 250MB -f dshow -pixel_format yuv420p -video_size 960x720 -i video="Logitech HD Pro Webcam C920" -c:v libx264 -crf 20 -pix_fmt yuv420p out.m3u8
我很确定编码匹配 requirements javafx 的,因为如果我将输出容器从 m3u8 更改为 mp4,则使用完全相同的 ffmpeg 命令播放视频时不会出现问题。
这是 m3u8 文件的 ffprobe 的输出:
Input #0, hls,applehttp, from 'out.m3u8':
Duration: 00:00:24.23, start: 1.466667, bitrate: 0 kb/s
Program 0
Metadata:
variant_bitrate : 0
Stream #0:0: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p, 960x720, 30 fps, 30 tbr, 90k tbn, 60 tbc
对于 mp4 文件:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'out.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf57.41.100
Duration: 00:01:04.93, start: 0.000000, bitrate: 1676 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 960x720, 1673 kb/s, 30 fps, 30 tbr, 10000k tbn, 60 tbc (default)
Metadata:
handler_name : VideoHandler
生成的 m3u8 文件如下所示:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:9
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:8.333322,
out0.ts
#EXTINF:8.333333,
out1.ts
#EXTINF:7.133322,
out2.ts
#EXTINF:0.433333,
out3.ts
#EXT-X-ENDLIST
更新:找到后this引用 m3u 文件,我认为问题在于该文件存储在本地,而不是通过 HTTP 传递。视频可以正常播放:
Media media = new Media("http://download.oracle.com/otndocs/products/javafx/JavaRap/prog_index.m3u8");
MediaPlayer player = new MediaPlayer(media);
player.setAutoPlay(true);
mediaView.setMediaPlayer(player);
但是在我下载了引用 m3u 及其所有段并尝试像这样打开本地文件后,错误再次发生:
File video = new File("H://Projects//Tools//ref//prog_index.m3u8");
Media media = new Media(video.toURI().toString());
MediaPlayer player = new MediaPlayer(media);
player.setAutoPlay(true);
mediaView.setMediaPlayer(player);
我试图更改我的 m3u 文件,以便使用绝对路径引用这些段。我尝试了不同的符号(
H:\f\out0.ts
, H:/f/out0.ts
, H://f//out0.ts
, file:/H:/f/out0.ts
, file:///H:/f/out0.ts
),但我无法让它工作。
最佳答案
不幸的是,您似乎在这里运气不佳。这是 JDK 内部的相关逻辑:
在 com.sun.media.jfxmedia.locator.Locator
public ConnectionHolder createConnectionHolder() throws IOException {
// [...] cache lookup elided
ConnectionHolder holder;
if ("file".equals(scheme)) {
holder = ConnectionHolder.createFileConnectionHolder(uri);
} else if (uri.toString().endsWith(".m3u8") || uri.toString().endsWith(".m3u")) {
holder = ConnectionHolder.createHLSConnectionHolder(uri);
}
如您所见,第一个检查是针对
file
方案,此时您会被毫不客气地发送到处理实际媒体文件的逻辑。 HLS 播放列表处理由稍后检查文件扩展名 .m3u8
触发但是到那时为时已晚,您的本地文件已匹配先前的条件并发送到错误的位置。您可能会争辩说这是一个错误并将其归档,尽管它有点极端。
关于ffmpeg - JavaFX MediaPlayer 无法播放本地 m3u8 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43759923/