This project provides additional modules needed to use VLC from within a NaCl or PNaCl module. With this project, built for PNaCl, VLC is able to play media within abritrary websites (ie doesn't require use of a Chrome Store app).
Building on Windows will likely be impossible without use of MSYS. The author uses Linux, and while an effort by the author was taken to ensure the most platform independent instructions, the reader's mileage my vary if their platform is not Linux. Fortunately, Linux is free and so is VirtualBox (and PNaCl is platform/arch independent by design).
Before we can get started, we need to procure a few things from the Internet:
- From the NaCl SDK,
pepper_43
or newer. - webports
$ ./configure && ./compile --arch $ARCH --pepper-root $NACL_SDK_ROOT --webports-root $WEBPORTS_ROOT
Where $ARCH
is one of le32
, i686
, x86_64
, or arm
, $NACL_SDK_ROOT
points to your Pepper SDK root, and $WEBPORTS_ROOT
points to the root of your
webports
checkout. compile
will fetch and build the needed webports
packages for you.
<script async>
var getVlc = null;
var restartVlc = null;
(function() {
"use strict";
var embed = null;
var parent = document.getElementById('video');
restartVlc = function() {
if(embed != null) {
parent.removeChild(embed);
}
embed = document.createElement('embed');
embed.setAttribute('id', 'vlc');
embed.setAttribute('src', 'vlc-debug.nmf');
embed.setAttribute('type', 'application/x-nacl');
// Comment out the following two lines if debugging VLC
embed.setAttribute('src', 'vlc-release.nmf');
embed.setAttribute('type', 'application/x-pnacl');
embed.setAttribute('width', '100%');
embed.setAttribute('height', '100%');
embed.addEventListener('load', function() {
var instance = new VlcInstance(embed);
getVlc = function() {
return instance;
};
});
parent.appendChild(embed);
};
restartVlc();
})();
</script>
Place the above after the #video element. It will create a single VLC
instance. Call restartVlc()
to restart the instance. getVlc()
will return
the instance object. Info on the API present is documented below.
Additionally, extra/ppapi-control.js
should be included in the <head>
of
the HTML document. ppapi-control.js
is internally versioned (ie all messages
sent to VLC specify which API version to use), so it is safe to copy where ever
needed.
Consult the nmf documentation for info on how to create vlc-debug.nmf
and
vlc-release.nmf
.
Using getVlc()
from above (all of these are static, in the C++ class sense):
getVlc().input
-- Stuff apropos to the current input (as defined inlibvlccore
).getVlc().input.state
-- Access to the backendinput_thread_t
state. Mostly for debugging; usegetVlc().playlist.state
instead.getVlc().input.rate
-- Get or set the current playback rate. Must be positive.getVlc().input.item
-- Get the currently playing item orundefined
if nothing is playing.getVlc().input.position
-- Get or set the current position of the media. Range: [0.0f, 1.0f]. Float.getVlc().input.time
-- Get or set the current time index of the media. Array [seconds, nanoseconds].getVlc().input.length
-- Get the length of the currently playing item. Array [seconds, nanoseconds].getVlc().input.video
-- Stuff relevant to videos only.getVlc().input.video.nextFrame()
-- Pause media and display next frame.getVlc().input.video.prevFrame()
-- Pause media and display previous frame. MASSIVELY slow. Won't function correctly without precise indexes.
getVlc().playlist
-- Stuff apropos to the playlist.getVlc().playlist.audio
-- Stuff relevant to videos only (the volume level is manage via playlist interfaces, so the author put it here).getVlc().playlist.audio.muted
-- Get or set the mute toggle. Boolean.getVlc().playlist.audio.volume
-- Get or set the audio volume. Values > 1.0f will result in amplification. Float.
getVlc().playlist.clear()
-- Clear all items from the playlist.getVlc().playlist.enqueue()
-- Add item(s) to the playlist. Accepts a single URL or an array of URLs. URLs must be absolute and prefixed withhttp://
.getVlc().playlist.dequeue()
-- Remove item(s) from the playlist. Accepts a singleplaylist_item_id
or an array of them.playlist_item_id
can be found in an element ofgetVlc().playlist.items
.getVlc().playlist.items
-- Gets the list of items in the playlist.getVlc().playlist.next()
-- Stop playing the current item and set the current item to the next in the playlist.getVlc().playlist.prev()
-- Stop playing the current item and set the current item to the prev in the playlist.getVlc().playlist.play()
-- Start playlist playback.getVlc().playlist.pause()
-- Pause playback.getVlc().playlist.stop()
-- Stop playlist playback. Does not reset the playlist to the beginning.getVlc().playlist.status
-- Get the current playback status. Will be one ofgetVlc().playlist.STOPPED_STATUS
,getVlc().playlist.RUNNING_STATUS
, orgetVlc().playlist.PAUSED_STATUS
. Convenience functions aregetVlc().playlist.isStopped()
,getVlc().playlist.isRunning()
, andgetVlc().playlist.isPaused()
, respectively.getVlc().playlist.repeating
-- If true, repeat the current item.getVlc().playlist.looping
-- If true, repeat the whole playlist.
getVlc().sys
-- Stuff related to VLC under the hood.getVlc().sys.log_level
-- Get or set log filtering level. Range: [0, 4].getVlc().sys.version
-- Get an object with various details about version of the VLC instance.getVlc().sys.purge_cache()
-- Purgeppapi-access
' media cache. All or nothing.
Note: getVlc().this_is_a_method()
&& getVlc().this_is_a_property
. All
methods accept a callback parameter as the last argument. The callback will be
called asynchronously with the results. Status codes mimic HTTP status codes, ie
a return_code
with a value of 200
indicates success. If no callback is
provided, any and all errors will be ignored with a warning printed to the
devtools console.
There is a limited selection of event one can listen for on the playlist
and
input
objects. Add a callback listener by calling addEventListener(loc, callback)
and
remove it with removeEventCallback(loc, callback)
on the object.
playlist
events (new values are in the new_value
key, old in old_value
):
input-current
: signals when the playlist input thread object (and thus the item) has changed. If the value is 0, the input was stopped and removed.
input
events (new values are in the value
key):
state
-- integer.dead
-- no value.rate
-- SeegetVlc().input.rate
.position
-- SeegetVlc().input.position
.length
-- SeegetVlc().input.length
.chapter
-- no value.program
-- no value.es
-- no value.teletext
-- no value.record
-- no value.item-meta
-- SeegetVlc().input.item
.item-info
-- no value.item-name
-- no value.item-epg
-- no value.statistics
-- no value.signal
-- no value.audio-delay
-- no value.subtitle-delay
-- no value.bookmark
-- no value.cache
-- Double, % buffered.aout
-- no value.vout
-- no value.
Example: getVlc().input.addEventListener('position', function(event) { console.log(event); }
This JS API is fully versioned, so the included ppapi-control.js
can be copied
into your projects source control and updated only when desired.
Take a look at ppapi-control.js
in extras/ppapi-control.js
for more info.
- XXX: There are no tests!
- The author hasn't extensively tested
ppapi-access.cpp
on slow connections (ie not localhost). - NaCl's service runtime/IRT doesn't support clock selection (though getting the monotonic time is still possible). Currently, vlc translates absolute monotonic time into absolute realtime time when waiting on condition vars. This is clearly bad if the system clock time travels.
- Something takes the address of
memcpy
(which is technically undefined behaviour). However, Clang doesn't error on this, and instead inserts a wrapper named exactlymemcpy
. LLVM then overwrites the real version of memcpy (ie what calls to @llvm.memcpy turn into) with the wrapper that Clang inserted, resulting in a infinite loop/stack exhaustion. - Although code to manage multiple simultaneous instances is present, the author has not tested any of it in any capacity.
- Use
PPB_VideoDecoder
to decode videos. This will delegate decoding to hardware, and should greatly reduce the CPU usage of many videos.