Как я могу создать объект Media в javafx из массива байтов?

Я пытаюсь воспроизвести мультимедиа в javafx, используя MediaView, MediaPlayer и Media. Класс Media, похоже, принимает только источник URL-адреса для создания мультимедиа (который используется для передачи в MediaPlayer). Я хотел бы передать кэшированный массив байтов для создания объекта Media. Это поддерживается?

Мне удалось воспроизвести мультимедиа, используя URL-адрес и файл, но не массив байтов.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Из медиа-документа «Поддерживаются только URI HTTP, HTTPS, FILE и JAR». Итак, вам нужно передать свои байты от провайдера по одному из этих протоколов.

Вы можете запустить встроенный http-сервер и обслуживать его байты по протоколу http.

Или вы можете использовать файловую систему в памяти, обслуживающую байты с использованием файловых или jar-протоколов.

Пример встроенного http-сервера, обслуживающего контент из байтов в памяти.

В примере используются типы изображений mime, но обслуживание других типов контента для мультимедиа будет работать, если входной поток корректен (например, представляет правильно закодированные данные mp3 или mp4).

Спасибо за интересный материал. Я попробую файловую систему в памяти.

Jeff 24.08.2024 16:50

Просто интересно, как будет выглядеть решение, использующее файловую систему в памяти?

Slaw 25.08.2024 07:27

Самый простой вариант — записать байты в файл, а затем передать URI file конструктору Media.

public Media toMediaFile(byte[] bytes, String extension) throws IOException {
  Objects.requireNonNull(bytes);
  if (extension.isBlank())
    throw new IllegalArgumentException("blank extension");
  var file = Files.createTempFile("jfxmedia", "." + extension);
  Files.write(file, bytes, StandardOpenOption.WRITE);
  return new Media(file.toUri().toString());
}

Однако, если вы хотите хранить байты мультимедиа в памяти, то я думаю, что самым простым решением будет одно из предложений в ответе Jewelsea : создать встроенный сервер для обслуживания мультимедиа через HTTP. Для этого вы можете использовать модуль jdk.httpserver . Пример использования этого модуля приведен в другом ответе Jewelsea (ссылка на который содержится в их ответе на этот вопрос). Но вот еще один пример, более специфичный для вашего варианта использования.

Серверный код

Обратите внимание, что этот пример сервера не реализован для обработки HTTP-заголовка Range. Я считаю, что реализация мультимедиа JavaFX будет использовать этот заголовок, по крайней мере, в некоторых случаях. Возможно, вы захотите реализовать эту функциональность на сервере.

Кроме того, возможно, будет проще найти более многофункциональную библиотеку HTTP-сервера и добавить ее в свой проект в качестве зависимости, чем использовать модуль jdk.httpserver. Хотя последнего должно быть достаточно для простого сервера, который существует только для обслуживания массивов byte[] через HTTP на локальном хосте.

InMemoryMedia.java

package com.example;

public record InMemoryMedia(byte[] bytes, String contentType) {
   
  public int contentLength() {
    return bytes.length;
  }
}

InMemoryMediaServer.java

package com.example;

import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

public class InMemoryMediaServer implements AutoCloseable {

  private final HttpServer httpServer;

  public InMemoryMediaServer(int port) throws IOException {
    var address = new InetSocketAddress(InetAddress.getLoopbackAddress(), port);
    httpServer = HttpServer.create(address, 1);
    httpServer.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
    httpServer.start();
  }

  public RegistrationKey registerMedia(String path, InMemoryMedia media) {
    Objects.requireNonNull(path);
    Objects.requireNonNull(media);

    var context = httpServer.createContext(path, new InMemoryMediaHandler(media));
    return new RegistrationKey(context);
  }

  @Override
  public void close() {
    httpServer.stop(0);
  }

  public class RegistrationKey {

    private final AtomicBoolean unregistered = new AtomicBoolean();
    private final HttpContext context;

    RegistrationKey(HttpContext context) {
      this.context = context;
    }

    public URI uri() {
      var addr = httpServer.getAddress();

      var scheme = "http";
      var host = addr.getAddress().getHostAddress();
      int port = addr.getPort();
      var path = path();

      try {
        return new URI(scheme, null, host, port, path, null, null);
      } catch (URISyntaxException ex) {
        throw new IllegalArgumentException(ex);
      }
    }

    public String path() {
      return context.getPath();
    }

    public void unregister() {
      if (unregistered.compareAndSet(false, true)) {
        httpServer.removeContext(context);
      }
    }
  }
}

InMemoryMediaHandler.java

package com.example;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import java.io.IOException;

final class InMemoryMediaHandler implements HttpHandler {

  private final InMemoryMedia media;

  InMemoryMediaHandler(InMemoryMedia media) {
    this.media = media;
  }

  @Override
  public void handle(HttpExchange exchange) throws IOException {
    switch (exchange.getRequestMethod()) {
      case "HEAD" -> {
        setResponseHeaders(exchange.getResponseHeaders());
        exchange.sendResponseHeaders(200, -1);
      }
      case "GET" -> {
        setResponseHeaders(exchange.getResponseHeaders());
        exchange.sendResponseHeaders(200, media.contentLength());
        try (var out = exchange.getResponseBody()) {
          out.write(media.bytes());
        }
      }
      default -> exchange.sendResponseHeaders(405, -1);
    }
  }

  private void setResponseHeaders(Headers headers) {
    headers.set("Accept-Ranges", "none");
    headers.set("Content-Type", media.contentType());
    headers.set("Content-Length", Integer.toString(media.contentLength()));
  }
}

Код приложения

Некоторый код, показывающий, как использовать класс InMemoryMediaServer.

Main.java

package com.example;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;

public class Main extends Application {

  private InMemoryMediaServer server;
  private String mediaUri;

  @Override
  public void init() throws Exception {
    server = new InMemoryMediaServer(8888);

    var key = server.registerMedia("/test", getInMemoryMedia());
    mediaUri = key.uri().toString();
  }

  private InMemoryMedia getInMemoryMedia() {
    throw new UnsupportedOperationException("not implemented"); // TODO: Implement
  }

  @Override
  public void start(Stage primaryStage) {
    var media = new Media(mediaUri);
    media.setOnError(() -> media.getError().printStackTrace());

    var player = new MediaPlayer(media);
    player.setAutoPlay(true);

    primaryStage.setScene(new Scene(new StackPane(new MediaView(player))));
    primaryStage.show();
  }

  @Override
  public void stop() {
    server.close();
  }
}

Другие вопросы по теме