web-dev-qa-db-ja.com

JavaFXでWebViewをミュートしますか?

とてもシンプルです。 JavaFX WebViewのボリュームをミュートまたは制御することは可能ですか?私はしばらくグーグルで検索しましたが、これについての言及は見つかりませんでした。 WebViewWebEngineのコードを調べましたが、音量の制御については何もありません。

動作してサウンドを生成するには、同じアプリ内に他のMediaPlayerが必要なので、アプリケーション全体をミュートすることはできません。

30
pupeno

それがAPIの欠落している部分であることは誰もが知っていますが、それを自分で実装するにはどうすればよいでしょうか。 WebViewが実際に何であるかを考えてみましょう:

埋め込みブラウザコンポーネントはWebKitに基づいています[...]デフォルトでは、WebKitはWebページのレンダリングをサポートしていません。 HTMLコンテンツをレンダリングおよび表示するために、OracleはJava Graphics 2D API。を使用して独自のレンダラーを作成する必要がありました。

これは、エンジンに実装が必要であることを意味します。 WebKitの実装は_com.Sun.webkit_パッケージにあります(クラスのかなりの部分はネイティブ呼び出しのラッパーにすぎません)。もちろん、ほとんどの場合、_com.Sun.*_クラスを使用したくありませんが、現在JavaFXを使用しているので、実際には問題ではありません。

Sunのソースに少しジャンプすると、次のようないくつかの抽象的なオーディオメソッドで_WCMediaPlayer.class_を見つけることができます。

_protected abstract void setRate(float rate);
protected abstract void setVolume(float volume);
protected abstract void setMute(boolean mute);
protected abstract void setSize(int w, int h);
protected abstract void setPreservesPitch(boolean preserve);
_

Javaを召し上がれ、あなたに電話させてください。

_volumeMethod = WCMediaPlayer.class.getDeclaredMethod("setVolume", float.class);
volumeMethod.setAccessible(true);
_

ご協力に感謝しますが、どうすればWCMediaPlayerインスタンスを取得できますか? new WCMediaPlayerImpl()への参照を確認する必要があります。ガッチャ! _WCGraphicsManager.class_はfwkCreateMediaPlayer()メソッドによってMediaPlayerを作成します。結局のところ、ポインターとインスタンスをrefMapに配置します。

_Field refMapField = WCGraphicsManager.class.getDeclaredField("refMap");
refMapField.setAccessible(true);
_

幸い、マネージャーはインスタンスを取得するためにgetGraphicsManager()メソッドを公開しています。

_WCGraphicsManager graphicsManager = WCGraphicsManager.getGraphicsManager();
refMap = (Map<Integer, Ref>) refMapField.get(graphicsManager);
_

キャッチされたマップにはRefインスタンスが含まれているため(他の_WC*_インスタンスもあります)、それらをフィルタリングする必要があります。

_Collection<WCMediaPlayer> mediaPlayers = refMap.values().stream()
    .filter(ref -> ref instanceof WCMediaPlayer)
    .map(ref -> (WCMediaPlayer) ref)
    .collect(Collectors.toList());
_

あなたはおそらく実用的な例を期待しているので、ここに私が使用したコードがあります:

WebEngineTest Preview

_public class WebEngineTest extends Application {

    private Map<Integer, Ref> refMap;
    private Method volumeMethod;

    @Override
    @SuppressWarnings("unchecked")
    public void start(Stage primaryStage) throws Exception {
        WebView webView = new WebView();
        WebEngine engine = webView.getEngine();
        engine.load("https://www.youtube.com/watch?v=hRAZBSoAsgs");

        Field refMapField = WCGraphicsManager.class.getDeclaredField("refMap");
        refMapField.setAccessible(true);

        volumeMethod = WCMediaPlayer.class.getDeclaredMethod("setVolume", float.class);
        volumeMethod.setAccessible(true);

        WCGraphicsManager graphicsManager = WCGraphicsManager.getGraphicsManager();
        refMap = (Map<Integer, Ref>) refMapField.get(graphicsManager);

        Button button = new Button("Volume");
        button.setOnAction(event -> setVolume(0.1f));

        Group group = new Group();
        group.getChildren().addAll(webView, button);

        Scene scene = new Scene(group, 625, 625);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public void setVolume(float volume) {
        Collection<WCMediaPlayer> mediaPlayers = this.getMediaPlayers();
        mediaPlayers.forEach(mediaPlayer -> setVolumeMethod(mediaPlayer, volume));
    }

    private void setVolumeMethod(Object instance, Object... args) {
        try {
            volumeMethod.invoke(instance, args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Collection<WCMediaPlayer> getMediaPlayers() {
        return refMap.values().stream()
                .filter(ref -> ref instanceof WCMediaPlayer)
                .map(ref -> (WCMediaPlayer) ref)
                .collect(Collectors.toList());
    }

}
_

最後に、これらの呼び出しの順序について覚えておいてください。たとえば、エンジンの状態がrefMapになるか、グラフィックの場合はWCGraphicsManager.getGraphicsManager()Refsを返すまで、SUCCEEDEDにはすべてのnullが含まれません。要素はまったく作成されません。

おそらく、最善の方法はこれらのソリューションを組み合わせることです。シーンリオとJavaFXによって提供される貧弱なAPIがなければ、Webテクノロジーミックスをサポートすることは困難です。 Chromiumなどの別のブラウザを埋め込んでみることもできます。

9
dzikoysk

現在、WebViewまたは-にサウンドシステム用の[〜#〜] no [〜#〜]のようなAPIがあります。 WebEngineコントロール。

ただし、JavaScriptを挿入して、サウンドをミュート/ミュート解除および制御することはできます。このメソッドは、HTML5要素<ビデオ>または<オーディオ>でのみ機能します)、Flashまたは[〜#〜] js [〜#〜]初期化されたオーディオは一般的に制限することは不可能です。私はあなたのためにサンプルプログラムを作成しました:

WebView mute/unmute - program

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        WebView myWebView = new WebView();
        WebEngine engine = myWebView.getEngine();
        //dirty code xD

        Button btn = new Button("Load Youtube");
        btn.setOnAction((ActionEvent event) -> engine.load("http://www.youtube.com/embed/A6hrw6KGdtM?autoplay=1"));

        Button btn2 = new Button("Mute");
        btn2.setOnAction((ActionEvent event) -> engine.executeScript(getJSAudioVideo(true)));

        Button btn3 = new Button("Unmute");
        btn3.setOnAction((ActionEvent event) -> engine.executeScript(getJSAudioVideo(false)));

        Button btn4 = new Button("+");
        btn4.setOnAction((ActionEvent event) -> engine.executeScript(getJSSoundVolume(0.9)));

        Button btn5 = new Button("-");
        btn5.setOnAction((ActionEvent event) -> engine.executeScript(getJSSoundVolume(0.1)));

        VBox root = new VBox();
        root.getChildren().addAll(myWebView, btn, btn2, btn3, btn4, btn5);

        Scene scene = new Scene(root, 800, 500);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Mute a WebView in JavaFX, is it possible?");

        primaryStage.show();
    }

    /**
     * @param mute
     * @return JS code for mute/unmute
     */
    public String getJSAudioVideo(boolean mute){
        return "var videos = document.querySelectorAll('video'),\n" +
                "    audios = document.querySelectorAll('listen');\n" +
                "    [].forEach.call(videos, function(video) { video.muted = "+String.valueOf(mute)+"; });\n" +
                "    [].forEach.call(audios, function(audio) { audio.muted = "+String.valueOf(mute)+"; });";
    }

    /**
     * @param volume
     * @return JS code for setting volume sound
     */
    public String getJSSoundVolume(double volume){
        //max 1.0, min 0.0 Default: 1.0
        double _volume = (volume > 1.0 || volume < 0.0) ? 1.0 : volume;

        return "var videos = document.querySelectorAll('video'),\n" +
                "    audios = document.querySelectorAll('listen');\n" +
                "    [].forEach.call(videos, function(video) { video.volume = "+String.valueOf(_volume)+"; });\n" +
                "    [].forEach.call(audios, function(audio) { audio.volume = "+String.valueOf(_volume)+"; });";

    }

    public static void main(String[] args) {
        launch(args);
    }
}
11

WebViewについて言うと、これはNodeクラスの拡張です。WebEngineオブジェクトをカプセル化し、HTMLコンテンツを組み込みます。アプリケーションのシーンに追加し、エフェクトと変換を適用するためのプロパティとメソッドを提供します。WebViewオブジェクトで呼び出されたgetEngine()メソッドは、それに関連付けられたWebエンジンを返します。 ソース

そのため、WebViewは現在、ミュート/ボリューム制御用のAPIをサポートしていません。 MediaPlayerにロードされたコンテンツをミュートするAPIがあります(つまり、)

MutePropertyのステータスを取得するには: ソースリンク

public BooleanProperty muteProperty()

ミュート状態を確認するには: ソースリンク

public final boolean isMute()

ビデオをミュートするには、値をtrueに設定します。 ソースリンク

public final void setMute(boolean value)

または、JavaScript APIを使用して、ビデオ/オーディオをミュートします。

getElementById("Your video Id");

コントロールをミュートに設定します:

vid.muted = true;

ソースリンク

6
Subash J