Git Product home page Git Product logo

Comments (21)

TRPB avatar TRPB commented on August 12, 2024 4

If anyone's interested, I spent about half an hour getting some basic functionality working without react. This may be a helpful starting point for anyone interested:

  var embed = document.createElement('embed');
  embed.type = 'application/x-mpvjs';

  document.body.insertBefore(embed, document.body.firstChild);


  embed.addEventListener('message', function(recv) {

    if (recv.data.type == 'ready') {
        var type = 'command';
       var data = ['loadfile', '/home/tom/tmp/mpv.js/example/tos.mkv'];
       const send = {type, data};
       embed.postMessage(send);
    }
  });

Take a look at index.js for the other commands, but this should be helpful for anyone else wanting a simple example.

from mpv.js.

Kylart avatar Kylart commented on August 12, 2024 4

If this helps, I made a Vue component fully compatible:

<script>
export default {
  name: 'MpvWrapper',

  data: () => ({
    style: {
      display: 'block',
      width: '100%',
      height: '100%',
      'pointer-events': 'none' // Allows cursor to be handled directly by MPV?
    },
    plugin: null,
    mimeType: 'application/x-mpvjs',

    mouseClick: {
      left: 0,
      timeout: null
    }
  }),

  props: {
    onPropertyChange: {
      type: Function,
      default: () => {}
    },
    onReady: {
      type: Function,
      default: () => {}
    }
  },

  mounted () {
    this.$el.addEventListener('message', this._handleMessage)

    window.addEventListener('keydown', this.keypress)

    this.property('cursor-autohide', 'always')
  },

  beforeDestroy () {
    window.removeEventListener('keydown', this.keypress)
  },

  methods: {
    /**
     * Send a command to the player.
     *
     * @param {string} cmd - Command name
     * @param {...*} args - Arguments
     */
    command (cmd, ...args) {
      args = args.map(arg => arg.toString())
      this._postData('command', [cmd].concat(args))
    },

    /**
     * Set a property to a given value.
     *
     * @param {string} name - Property name
     * @param {*} value - Property value
     */
    property (name, value) {
      this._postData('set_property', { name, value })
    },

    /**
     * Get a notification whenever the given property changes.
     *
     * @param {string} name - Property name
     */
    observe (name) {
      this._postData('observe_property', name)
    },

    /**
     * Send a key event through mpv's input handler, triggering whatever
     * behavior is configured to that key.
     *
     * @param {KeyboardEvent} event
     */
    keypress ({ key, shiftKey, ctrlKey, altKey }) {
      // Don't need modifier events.
      if ([
        'Escape', 'Shift', 'Control', 'Alt',
        'Compose', 'CapsLock', 'Meta'
      ].includes(key)) return

      if (key.startsWith('Arrow')) {
        key = key.slice(5).toUpperCase()
        if (shiftKey) {
          key = `Shift+${key}`
        }
      }
      if (ctrlKey) {
        key = `Ctrl+${key}`
      }
      if (altKey) {
        key = `Alt+${key}`
      }

      // Ignore exit keys for default keybindings settings.
      if ([
        'q', 'Q', 'ESC', 'POWER', 'STOP',
        'CLOSE_WIN', 'Ctrl+c',
        'AR_PLAY_HOLD', 'AR_CENTER_HOLD'
      ].includes(key)) return

      this.command('keypress', key)
    },

    _postData (type, data) {
      this.$el.postMessage({ type, data })
    },
    _handleMessage ({ data: { type, data } }) {
      const actions = {
        property_change: () => this.onPropertyChange(data.name, data.value),
        ready: () => this.onReady(this)
      }
      const action = actions[type]

      action && action()
    }
  },

  render (h) {
    return this.$createElement('embed', {
      ref: 'plugin',

      staticClass: 'mpv-wrapper',
      style: this.style,
      attrs: {
        type: this.mimeType
      }
    })
  }
}
</script>

It's highly inspired by the React component given by mpv.js. It can be used just like any component:

<template>
  <mpv-wrapper 
      :onReady='handleMPVReady',
      :onPropertyChange='handlePropertyChange'
  />
</template>

<script>
import MpvWrapper from '/path/to/wrapper.vue'

export default {
  components: { MpvWrapper },

  data: () => ({
    mpv: null
  }),

  methods: {
    handleMPVReady (mpv) {
      this.mpv = mpv
      console.log('Player is ready')
    },
    handlePropertyChange (name, value) {
      console.log('Property change:', name, value)
    }
  }
}
</script>

If needed, I can PR this into the lib. Hope it'll help someone 😄

from mpv.js.

Kagami avatar Kagami commented on August 12, 2024 2

Yea, it would be nice, not everyone using React.

from mpv.js.

biaogewow avatar biaogewow commented on August 12, 2024 2

I implement the class.

(function () {
    function MPV (embed, options, callback) {
        var thiz = this;

        var hwdec = options.hwdec == true ? true : false,
            src = options.src || "",
            loop = options.loop == true ? true : false,
            volume = options.volume != null ? options.volume : 100,
            autoplay = options.autoplay == true ? true : false;

        thiz.mpv = embed;
        thiz.mpv.type = 'application/x-mpvjs';

        thiz.mpv.addEventListener("message", function (e) {
            if (e.data.type == 'ready') {
                thiz.loadfile(src);

                if (hwdec) thiz.setProperty("hwdec", "vaapi-copy");
                if (volume != null) thiz.setVolume(volume);
                if (loop) thiz.setProperty("loop-file", "inf");
                if (autoplay) thiz.play();

                thiz.mpv.postMessage({
                    type: 'observe_property',
                    data: "duration"
                });

                thiz.mpv.postMessage({
                    type: 'observe_property',
                    data: "time-pos"
                });

                thiz.mpv.postMessage({
                    type: 'observe_property',
                    data: "pause"
                });

                thiz.mpv.postMessage({
                    type: 'observe_property',
                    data: "eof-reached"
                });

                callback && callback({
                    "name": "ready"
                });
            }
            else if (e.data.type == "property_change"){
                if (e.data.data.name == "duration") {
                    callback && callback({
                        "name": "duration",
                        "value": e.data.data.value,
                    });
                }
                else if (e.data.data.name == "time-pos") {
                    callback && callback({
                        "name": "progress",
                        "value": e.data.data.value,
                    });
                }
                else if (e.data.data.name == "pause" && !e.data.data.value) {
                    callback && callback({
                        "name": "play",
                    });
                }
                else if (e.data.data.name == "pause" && e.data.data.value) {
                    callback && callback({
                        "name": "pause",
                    });
                }
                else if (e.data.data.name == "eof-reached" && e.data.data.value) {
                    callback && callback({
                        "name": "ended",
                    });
                }
            }
        });

        return thiz;
    }

    MPV.prototype.setProperty = function (name, value) {
        this.mpv.postMessage({
            type: 'set_property',
            data: {
                name: name,
                value: value,
            }
        });

        return this;
    };

    MPV.prototype.sendCommand = function (name, value) {
        var data = [];
        if (name) data.push(name);
        if (value) data.push(value);

        this.mpv.postMessage({
            type: 'command',
            data: data,
        });

        return this;
    };

    MPV.prototype.loadfile = function (src, autoplay = true) {
        this.sendCommand("loadfile", src);
        if (!autoplay) {
            this.pause();
        }
        else {
            this.play();
        }

        return this;
    };

    MPV.prototype.play = function () {
        this.setProperty("pause", false);

        return this;
    };

    MPV.prototype.pause = function () {
        this.setProperty("pause", true);

        return this;
    };

    MPV.prototype.replay = function () {
        this.setProperty("time-pos", 0);
        this.setProperty("pause", false);

        return this;
    };

    MPV.prototype.setVolume = function (volume) {
        this.setProperty("volume", volume);

        return this;
    };

    MPV.prototype.destroy = function () {
        this.pause();
        this.sendCommand("stop", null);
        this.mpv.remove();
        return this;
    };

    window.MPV = MPV;
    return MPV;
})();

You can use it like this.

var video1 = "demo.mp4";
var video2 = "demo2.mp4";

var embed = document.createElement('embed');
embed.setAttribute('style','');
embed.style.width = '800px';
embed.style.height = '600px';
embed.style.position = 'absolute';
embed.style.top = 0;
embed.style.left = 0;
document.body.append(embed);

var mpv = new MPV(embed, {
    hwdec: true,
    src: "",
    loop: false, // if set 'true'. !!! no events <pause,ended>.
    volume: 100,  // 0 ~ 100
    autoplay: true,
}, function (e) {
    if (e.name == "ready") {
        console.info("mpv ready.");
        // TODO
        // ...
        mpv.loadfile(video1, true);
    }
    else if (e.name == "play") {
        console.info("play.");
    }
    else if (e.name == "duration") {
        console.info("duration is ", e.value);
    }
    else if (e.name == "progress") {
        // console.info("progress >>> ", e.value);
    }
    else if (e.name == "pause") {
        console.info("pause.");
    }
    else if (e.name == "ended") {
        console.info("ended.");
        mpv.loadfile(video2, true);
    }
});

from mpv.js.

tepaze avatar tepaze commented on August 12, 2024

Like me :-)

I'm trying to build an electron video player with non h264 capabilities, to build a video app around video, and it's not easy to find another component than WebChimera.js... The problem is WCJS is not sure for the futur, and had no hw decoding capability...

Perhaps it's electron that is not the right choice...

Hope you take time to build that Vanilla JS MPV component :-)

from mpv.js.

burketeralogics avatar burketeralogics commented on August 12, 2024

Yeah I second this one. I was able to read through the react component code and cobble something together to create an angular component. A vanilla example would have been very helpful though.

If I get a breather I'll look over my code for the component and see if I can break it down into something vanilla, it's a simple one, so a lot easier to use.

I'm using this for an Electron app (angular as the framework) and it works great so far. I needed multicast video playback as a requirement.

I spent time in WCJS hell, mpv.js is serious a blessing. It took me a day or so to get up and running, including compiling on an ancient distro. Easier to maintain, seems quicker, a bit more flexible, and works.

from mpv.js.

CanRau avatar CanRau commented on August 12, 2024

would love this too :D

and as a side note, I tried to vueify it but couldn't get require.resolve to work so far because I'm using webpack and it's returning the module id instead of like node returning the actual path
any ideas about this one?

from mpv.js.

guest271314 avatar guest271314 commented on August 12, 2024

@TRPB Can the code be used at Chromium browser?

from mpv.js.

TRPB avatar TRPB commented on August 12, 2024

If the extension will load, probably but it's rather pointless unless your site is an intranet site and you have control of all the installations.

from mpv.js.

guest271314 avatar guest271314 commented on August 12, 2024

@TRPB What mean is using mpv.js without an "extension". As Chromium uses a video decoder (for VP8 and VP9 and potentially in the future for any codec) that does not resize HTML <video> element when underlying frame resolution changes, only displays the pixel dimensions of the initial pixel dimensions in the container metadata. Am trying to determine if this code can be adjusted to substitute for HTML <video> element altogether on the main thread in HTML document.

from mpv.js.

Benny233 avatar Benny233 commented on August 12, 2024

If this helps, I made a Vue component fully compatible:

<script>
export default {
  name: 'MpvWrapper',

  data: () => ({
    style: {
      display: 'block',
      width: '100%',
      height: '100%',
      'pointer-events': 'none' // Allows cursor to be handled directly by MPV?
    },
    plugin: null,
    mimeType: 'application/x-mpvjs',

    mouseClick: {
      left: 0,
      timeout: null
    }
  }),

  props: {
    onPropertyChange: {
      type: Function,
      default: () => {}
    },
    onReady: {
      type: Function,
      default: () => {}
    }
  },

  mounted () {
    this.$el.addEventListener('message', this._handleMessage)

    window.addEventListener('keydown', this.keypress)

    this.property('cursor-autohide', 'always')
  },

  beforeDestroy () {
    window.removeEventListener('keydown', this.keypress)
  },

  methods: {
    /**
     * Send a command to the player.
     *
     * @param {string} cmd - Command name
     * @param {...*} args - Arguments
     */
    command (cmd, ...args) {
      args = args.map(arg => arg.toString())
      this._postData('command', [cmd].concat(args))
    },

    /**
     * Set a property to a given value.
     *
     * @param {string} name - Property name
     * @param {*} value - Property value
     */
    property (name, value) {
      this._postData('set_property', { name, value })
    },

    /**
     * Get a notification whenever the given property changes.
     *
     * @param {string} name - Property name
     */
    observe (name) {
      this._postData('observe_property', name)
    },

    /**
     * Send a key event through mpv's input handler, triggering whatever
     * behavior is configured to that key.
     *
     * @param {KeyboardEvent} event
     */
    keypress ({ key, shiftKey, ctrlKey, altKey }) {
      // Don't need modifier events.
      if ([
        'Escape', 'Shift', 'Control', 'Alt',
        'Compose', 'CapsLock', 'Meta'
      ].includes(key)) return

      if (key.startsWith('Arrow')) {
        key = key.slice(5).toUpperCase()
        if (shiftKey) {
          key = `Shift+${key}`
        }
      }
      if (ctrlKey) {
        key = `Ctrl+${key}`
      }
      if (altKey) {
        key = `Alt+${key}`
      }

      // Ignore exit keys for default keybindings settings.
      if ([
        'q', 'Q', 'ESC', 'POWER', 'STOP',
        'CLOSE_WIN', 'Ctrl+c',
        'AR_PLAY_HOLD', 'AR_CENTER_HOLD'
      ].includes(key)) return

      this.command('keypress', key)
    },

    _postData (type, data) {
      this.$el.postMessage({ type, data })
    },
    _handleMessage ({ data: { type, data } }) {
      const actions = {
        property_change: () => this.onPropertyChange(data.name, data.value),
        ready: () => this.onReady(this)
      }
      const action = actions[type]

      action && action()
    }
  },

  render (h) {
    return this.$createElement('embed', {
      ref: 'plugin',

      staticClass: 'mpv-wrapper',
      style: this.style,
      attrs: {
        type: this.mimeType
      }
    })
  }
}
</script>

It's highly inspired by the React component given by mpv.js. It can be used just like any component:

<template>
  <mpv-wrapper 
      :onReady='handleMPVReady',
      :onPropertyChange='handlePropertyChange'
  />
</template>

<script>
import MpvWrapper from '/path/to/wrapper.vue'

export default {
  components: { MpvWrapper },

  data: () => ({
    mpv: null
  }),

  methods: {
    handleMPVReady (mpv) {
      this.mpv = mpv
      console.log('Player is ready')
    },
    handlePropertyChange (name, value) {
      console.log('Property change:', name, value)
    }
  }
}
</script>

If needed, I can PR this into the lib. Hope it'll help someone 😄

hi, i try to use your code. but i failed. is there any other code or config i need to do?

from mpv.js.

Kylart avatar Kylart commented on August 12, 2024

I'm afraid I'd need more details here 🤔
Is the code online?

from mpv.js.

iffa avatar iffa commented on August 12, 2024

a quick browse of the fork network: https://github.com/pavladan/mpv.js

tested this fork, works perfectly with no react dependency 👍

from mpv.js.

guest271314 avatar guest271314 commented on August 12, 2024

FWIW am not sure why this question Playing audio files in unsupported codecs through tag over at SO received a "downvote" already. The case appears to be precisely solvable by using this repository. Tested loading both CAF and AMR at native mpv where plays both files are played.

from mpv.js.

anesuc avatar anesuc commented on August 12, 2024

@TRPB I've been trying to use your JS way. How do you even trigger play for example?

from mpv.js.

TRPB avatar TRPB commented on August 12, 2024

You need to use embed.postMessage() to send the command.

Here's a more complete solution I ended up using:

class MPV {
  constructor(container) {
    this.embed = document.createElement('embed');
    this.embed.type = 'application/x-mpvjs';
    this.events = {};

    container.appendChild(this.embed);

    this.embed.addEventListener('message', this.recv.bind(this));
  }

  property(name, value) {
    const data = {name, value};
    this._postData("set_property", data);
  }

  recv(event) {
    var type = event.data.type;
    if (this.events[type]) {
      this.events[type].bind(this)(event);
    }
  }

  _postData(type, data) {
    const msg = {type, data};
    this.node().postMessage(msg);
  }

  command(cmd, ...args) {
    args = args.map(arg => arg.toString());
    this._postData("command", [cmd].concat(args));
  }

  observe(name) {
    this._postData("observe_property", name);
  }

  keypress(key)  {
    this.command("keypress", key);
  }

  node() {
    return document.getElementsByTagName('embed')[0];
  }

  on(event, listener) {
    this.events[event] = listener;
  }
}

You can do things like

var mpv = new MPV(document.getElementById('video'));
mpv.property('deinterlace', 'yes');
mpv.property('vf', 'vf=vavpp:deint=motion-adaptive:interlaced-only=yes');

Properties are just the command line switches from here:
https://mpv.io/manual/master/

You can also send commands like loadfile:

mpv.command('loadfile', filename);

see here for a full list: https://mpv.readthedocs.io/en/latest/api.html

//play the video by unpausing
mpv.property('pause', false);

and receive events:

mpv.observe('duration');
mpv.on('duration', function(data) {
            console.log(data);
 });

or mimic a key press:

//toggle interlacing via key press
mpv.keypress('I');

For keys and commands see: https://github.com/kba/mpv-config/blob/master/input.conf

from mpv.js.

anesuc avatar anesuc commented on August 12, 2024

@TRPB Ok thanks for that! So do I put your first part of your code and replace the index.js in the module, then call the module the usual way then do the second part+ of your code in my actual app?

from mpv.js.

anesuc avatar anesuc commented on August 12, 2024

@TRPB I'm getting Uncaught TypeError: Cannot read property 'postMessage' of undefined for just calling the mpv.command. I replaced the Mpvjs class in the module with the new class you provided.

Then in the html:

`var {MPV} = require("mpv.js");

var mpv = new MPV(document.getElementById('video'));
//mpv.property('deinterlace', 'yes');
//mpv.property('vf', 'vf=vavpp:deint=motion-adaptive:interlaced-only=yes');
var filename = 'file:///home/...';
mpv.command('loadfile', filename);`

Edit: I think I have an idea whats going on. Modules can't access the DOM in Nw.Js. Not as easily anyway. Working on a workaround.
Edit 2: Fixed it looking through your complete code! Thanks

from mpv.js.

anesuc avatar anesuc commented on August 12, 2024

@biaogewow nice! I might give this a shot actually!

from mpv.js.

ChordMankey avatar ChordMankey commented on August 12, 2024

If this helps, I made a Vue component fully compatible:

@Kylart
I tried using your component exactly as is in Vue3 but I'm having some trouble and would appreciate some guidance.

I initially got an error on

    render (h) {
      return this.$createElement('embed', {
        ref: 'plugin',
  
        staticClass: 'mpv-wrapper',
        style: this.style,
        attrs: {
          type: this.mimeType
        }
      })
    }

because this.$createElement was apparently not a function but I was ableto fix that by importing 'h' from vue and calling that instead like so:

    render () {
      return h('embed', {
        ref: 'plugin',
  
        staticClass: 'mpv-wrapper',
        style: this.style,
        attrs: {
          type: this.mimeType
        }
      })
    }

But after that I'm getting an error here:

      _postData (type, data) {
        this.$e1.postMessage({ type, data })
      },

TypeError: Cannot read property 'postMessage' of undefined

I'm new to Electron, Vue, and JS so I'm not even sure what this.$e1.postMessage({ type, data }) is supposed to do but maybe it has to do with the 'this' reference being different for functions defined within the methods json?

from mpv.js.

kdreaming avatar kdreaming commented on August 12, 2024

@ChordMankey

      _postData (type, data) {
        this.$e1.postMessage({ type, data })
      },

emmmmmm, Looks like a misspelling
_postData (type, data) {this.$el.postMessage({ type, data })},

from mpv.js.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.