JavaFX MediaPlayer无法播放本地m3u8文件

时间:2017-05-03 12:22:59

标签: ffmpeg javafx-8 gstreamer hls m3u8

我想使用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.tsH:/f/out0.tsH://f//out0.tsfile:/H:/f/out0.tsfile:///H:/f/out0.ts),但我无法使其发挥作用。

1 个答案:

答案 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触发的,但到那时为时已晚,您的本地文件已匹配先前的条件并发送到错误的位置。您可能会认为这是一个错误并将其归档,尽管它有点像边缘情况。