我想使用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的构造函数内发生错误。此本机方法返回错误代码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
我很确定编码与javafx的requirements匹配,因为如果我将输出容器从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
),但我无法使其发挥作用。
答案 0 :(得分:2)
不幸的是,你好像在这里运气不好。这是来自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
触发的,但到那时为时已晚,您的本地文件已匹配先前的条件并发送到错误的位置。您可能会认为这是一个错误并将其归档,尽管它有点像边缘情况。