JavaFX MediaPlayer не может воспроизвести локальный файл m3u8

Я хочу показать прямой эфир с веб-камеры в моем приложении JavaFX с помощью MediaPlayer / MediaView. Моя попытка заключалась в том, чтобы использовать 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 при вызове GSTMediaPlayer.gstInitPlayer(). Этот собственный метод возвращает код ошибки 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, потому что если я изменю выходной контейнер с m3u8 на mp4, видео будет воспроизводиться без проблем с использованием той же самой команды ffmpeg.

Это результат ffprobe для файла m3u8:

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

Обновление: после того, как я нашел это ссылка на файл 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), но не смог заставить его работать.


person Lennart    schedule 03.05.2017    source источник
comment
Что в вашем файле m3u8? Может быть, ваша программа на Java не может найти сегменты?   -  person pvg    schedule 03.05.2017
comment
Совсем забыл добавить содержимое файлов. Обновил вопрос. Но я не думаю, что проблема в содержимом файлов, так как VLC может их без проблем воспроизвести.   -  person Lennart    schedule 03.05.2017
comment
«VLC может воспроизводить это без проблем» не так уж и полезен для вас, поскольку VLC будет воспроизводить многие вещи без проблем. Попробуйте поиграть со своим m3u, скажем, используйте абсолютные пути или URL-адреса файлов. Можете ли вы заставить работать любой m3u? и т.п.   -  person pvg    schedule 03.05.2017
comment
Я попытался возиться с m3u, но мне не удалось заставить его работать. Но мне удалось найти эталонный m3u, который работает нормально, когда на него ссылаются через http, но он не воспроизводит локальный файл после того, как я его загрузил (и все сегменты). Я обновил свой вопрос соответственно.   -  person Lennart    schedule 03.05.2017
comment
Здравствуйте! Вы нашли решение для воспроизведения локальных видеороликов m3u8? Мне оно нужно для моего приложения, где видеоролики m3u8 помещаются в локальную папку для воспроизведения видео. Не могли бы вы поделиться кодом для воспроизведения локальных видеороликов m3u8   -  person Chandramouli    schedule 24.02.2020


Ответы (2)


К сожалению, похоже, вам здесь не повезло. Это актуальная логика из недр JDK:

in 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, но к этому моменту уже слишком поздно, ваш локальный файл соответствует предыдущему условию и отправлен в неправильное место. Вы можете утверждать, что это ошибка, и записывать ее как таковую, хотя это немного крайний случай.

person pvg    schedule 03.05.2017
comment
Вы можете возразить, что это ошибка. Есть ли способ на самом деле воспроизвести плейлист? Мне так не кажется, поэтому он бросает вызов его функциональности. Не с чем спорить, определенно ошибка. - person Xerus; 19.10.2017
comment
@Xerus Если вы собираетесь спорить, спорить не о чем на сайте, который почти полностью состоит из аргументов ботаников, вы, возможно, будете немного удивлены :) - person pvg; 19.10.2017

Проблема заключается в том, что WebEngine обращается к локальной файловой системе [file: //]. И вот как это происходит.

//Started app [run-able .jar] and loaded a start page [resource in .jar] 
protected static String Some_Resource_in_a_JAR = "/jimmylandstudios/gui/index.html";
webEngine.load(getClass()
.getResource(" + Some_Resource_in_a_JAR + ")
.toExternalForm());
//So, you can forget using the file:// protocol now. 

Мой ответ был либо: используйте HTTP: сервер для любых созданных / измененных файлов. Или лучше,

//Avoid issue [accessing local files] 
protected static String Extracted_Resource_from_JAR = "/jimmylandstudios/gui/index.html";
webEngine.load("file://" + Extracted_Resource_from_JAR + ");

Проще говоря: Запустите WebEngine с протоколом file: //, если вы обращаетесь к локальным файлам.

person JimmyLandStudios    schedule 20.01.2020