├── .gitignore ├── LICENSE ├── README.md ├── examples ├── url.js └── vlc.js ├── lib ├── audio.js ├── libvlc.js ├── libvlc_types.js ├── media.js ├── media_enum.js ├── mediaplayer.js ├── util.js ├── video.js └── vlm.js ├── package.json └── vlc.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Timothy J Fontaine 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-vlc 2 | -------- 3 | 4 | node bindings for libvlc using ffi 5 | 6 | Currently this package depends on VLC >= 2.0.1, if you're on Mac OS X or Windows 7 | it tries to detect the location of your VLC installation and find libvlc under 8 | that. 9 | 10 | Usage 11 | ----- 12 | 13 | The ffi library can currently only be initialized once per process, so you can't 14 | use multiple versions of libvlc in the same process. 15 | 16 | The library tries to deduce the common location libvlc.so, `/usr/lib/libvlc.so` 17 | or `/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib` for instance. If 18 | your `libvlc` is named or versioned differently set `vlc.LIBRARY_PATHS` to the 19 | full path (including filename) of your preferred version. 20 | 21 | If libvlc isn't found after trying all of `vlc.LIBRARY_PATHS` it attempts to 22 | load from your systems normal library loading parameters (i.e. `/etc/ld.so/conf`) 23 | 24 | Searching is in array order, and is synchronous, but only happens on the first 25 | initialization i.e. the first time you call `var instance = new vlc('-I', 'dummy');` 26 | After the first successful initialization all dependent modules will use that 27 | file for interactions. 28 | 29 | For a quick example see `examples/vlc.js` 30 | 31 | There are some operations that vlc performs that are synchronous. Currently 32 | this library makes no attempt to work around such things, so be sure you know 33 | what you're doing. 34 | 35 | libvlc does have an event interface, but it is not as robust as nodes nor does 36 | it necessarily match the node pattern. 37 | 38 | There is not much documentation at the moment, use the source luke. For that 39 | matter, there's not much documentation around libvlc either. 40 | 41 | Events 42 | ------ 43 | 44 | Currently you can attach to the following events 45 | 46 | * Media -- odd bug, on osx you need to parseSync before attaching any handlers 47 | else the process freezes, I haven't investigated fully to understand why yet. 48 | * `MetaChanged` - callback receives metadata field name that changed 49 | * `SubItemAdded` - callback receives new media item 50 | * `DurationChanged` - callback receives the new duration 51 | * `ParsedChanged` - callback receives the new parsed state 52 | * `Freed` - callback receives media item that was freed (wtf this seems like a bad idea) 53 | * `StateChanged` - callback receives the new media state 54 | * MediaPlayer 55 | * `MediaChanged` - no argument 56 | * `NothingSpecial` - no argument 57 | * `Opening` - no argument 58 | * `Buffering` - callback receives percent full of cache 59 | * `Playing` - no argument 60 | * `Paused` - no argument 61 | * `Stopped` - no argument 62 | * `Forward` - no argument 63 | * `Backward` - no argument 64 | * `EndReached` - no argument 65 | * `EncounteredError` - no argument 66 | * `TimeChanged` - callback receives the new time (constantly update while playing) 67 | * `PositionChanged` - callback receives new position (constantly updated while playing) 68 | * `SeekableChanged` - callback receives truthy of seekable 69 | * `PausableChanged` - callback receives truthy of pausable 70 | * `TitleChanged` - callback receives truthy of title changed 71 | * `SnapshotTaken` - no argument 72 | * `LengthChanged` - no argument 73 | * `Vout` - callback receives the new number of vout channels 74 | 75 | -------------------------------------------------------------------------------- /examples/url.js: -------------------------------------------------------------------------------- 1 | var vlc = require('../vlc')([ 2 | '-I', 'dummy', 3 | '-V', 'dummy', 4 | '--verbose', '1', 5 | '--no-video-title-show', 6 | '--no-disable-screensaver', 7 | '--no-snapshot-preview', 8 | ]); 9 | 10 | var media = vlc.mediaFromUrl(process.argv[2]); 11 | media.parseSync(); 12 | 13 | media.track_info.forEach(function (info) { 14 | console.log(info); 15 | }); 16 | 17 | console.log(media.artist, '--', media.album, '--', media.title); 18 | 19 | var player = vlc.mediaplayer; 20 | player.media = media; 21 | console.log('Media duration:', media.duration); 22 | 23 | player.play(); 24 | var POS = 0.5 25 | player.position = POS; 26 | 27 | var poller = setInterval(function () { 28 | //console.log('Poll:', player.position); 29 | if (player.position < POS) 30 | return; 31 | 32 | try { 33 | if (player.video.track_count > 0) { 34 | player.video.take_snapshot(0, "test.png", player.video.width, player.video.height); 35 | } 36 | console.log('Media Stats:', media.stats); 37 | } catch (e) { 38 | console.log(e); 39 | } 40 | finally { 41 | if (player.position < 0.7) 42 | return; 43 | 44 | player.stop(); 45 | 46 | media.release(); 47 | vlc.release(); 48 | 49 | clearInterval(poller); 50 | } 51 | }, 100); 52 | 53 | /* 54 | setTimeout(function () { 55 | console.log('--- stoping ---', player.position); 56 | player.stop(); 57 | clearInterval(poller); 58 | }, media.duration + 100); 59 | */ 60 | -------------------------------------------------------------------------------- /examples/vlc.js: -------------------------------------------------------------------------------- 1 | var vlc = require('../vlc')([ 2 | '-I', 'dummy', 3 | '-V', 'dummy', 4 | '--verbose', '1', 5 | '--no-video-title-show', 6 | '--no-disable-screensaver', 7 | '--no-snapshot-preview', 8 | ]); 9 | 10 | var media = vlc.mediaFromFile(process.argv[2]); 11 | media.parseSync(); 12 | 13 | media.track_info.forEach(function (info) { 14 | console.log(info); 15 | }); 16 | 17 | console.log(media.artist, '--', media.album, '--', media.title); 18 | 19 | var player = vlc.mediaplayer; 20 | player.media = media; 21 | console.log('Media duration:', media.duration); 22 | 23 | player.play(); 24 | var POS = 0.5 25 | player.position = POS; 26 | 27 | var poller = setInterval(function () { 28 | console.log('Poll:', player.position); 29 | if (player.position < POS) 30 | return; 31 | 32 | try { 33 | if (player.video.track_count > 0) { 34 | player.video.take_snapshot(0, "test.png", player.video.width, player.video.height); 35 | } 36 | } catch (e) { 37 | console.log(e); 38 | } 39 | finally { 40 | player.stop(); 41 | 42 | media.release(); 43 | vlc.release(); 44 | 45 | clearInterval(poller); 46 | } 47 | }, 500); 48 | 49 | /* 50 | setTimeout(function () { 51 | console.log('--- stoping ---', player.position); 52 | player.stop(); 53 | clearInterval(poller); 54 | }, media.duration + 100); 55 | */ 56 | -------------------------------------------------------------------------------- /lib/audio.js: -------------------------------------------------------------------------------- 1 | var lib = require('./libvlc'); 2 | 3 | var util = require('./util'); 4 | 5 | var properties = { 6 | mute: { get: true, set: true }, 7 | volume: { get: true, set: true }, 8 | track_count: { get: true }, 9 | track: { get: true, set: true }, 10 | channel: { get: true, set: true }, 11 | delay: { get: true, set: true }, 12 | }; 13 | 14 | var Audio = function (mediaplayer) { 15 | util.makeProperties(this, mediaplayer, properties, 'libvlc_audio_'); 16 | 17 | Object.defineProperty(this, 'description', { 18 | get: function () { 19 | var ret = [], tmp, start; 20 | 21 | start = tmp = lib.libvlc_audio_get_track_description(mediaplayer); 22 | 23 | while(!tmp.isNull()) { 24 | tmp = tmp.deref(); 25 | ret.push({ 26 | id: tmp.i_id, 27 | name: tmp.psz_name, 28 | }); 29 | tmp = tmp.p_next; 30 | } 31 | 32 | if (!start.isNull()) 33 | lib.libvlc_track_description_list_release(start); 34 | 35 | return ret; 36 | }, 37 | }); 38 | 39 | this.toggle_mute = function () { 40 | lib.libvlc_audio_toggle_mute(mediaplayer); 41 | } 42 | }; 43 | 44 | module.exports = Audio; 45 | -------------------------------------------------------------------------------- /lib/libvlc.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var semver = require('semver'); 4 | 5 | var FFI = require('ffi'); 6 | var ref = require('ref'); 7 | 8 | var types = require('./libvlc_types'); 9 | var MediaStatsPtr = types.MediaStatsPtr; 10 | var ModuleDescriptionPtr = types.ModuleDescriptionPtr; 11 | var TrackDescriptionPtr = types.TrackDescriptionPtr; 12 | 13 | var check_library = function(p) { 14 | var l, v; 15 | 16 | try { 17 | l = new FFI.Library(p, {libvlc_get_version:['string',[]],}); 18 | v = l.libvlc_get_version().split(' ')[0]; 19 | if (semver.satisfies(v, '>= 2.0.1')) 20 | return l; 21 | } catch (e) { 22 | l = undefined; 23 | } 24 | 25 | return l; 26 | }; 27 | 28 | exports.initialize = function(paths) { 29 | var i, p, l, v; 30 | 31 | for (i = 0; i < paths.length; i++) { 32 | p = paths[i]; 33 | if (fs.existsSync(p)) { 34 | l = check_library(p); 35 | if (l) 36 | break; 37 | } 38 | } 39 | 40 | if (!l) { 41 | p = 'libvlc' 42 | check_library(p); 43 | } 44 | 45 | if (!l) 46 | throw new Error("Failed to find LibVLC >= 2.0.1, make sure it's installed and that you have set LIBRARY_PATHS"); 47 | 48 | module.exports = new FFI.Library(p, { 49 | libvlc_errmsg: ['string', []], 50 | libvlc_clearerr: ['void', []], 51 | libvlc_vprinterr: ['string', ['string', 'pointer']], 52 | libvlc_printerr: ['string', ['string']], 53 | libvlc_new: ['pointer', ['int32', 'pointer']], 54 | libvlc_release: ['void', ['pointer']], 55 | libvlc_retain: ['void', ['pointer']], 56 | libvlc_add_intf: ['int32', ['pointer', 'string']], 57 | libvlc_set_exit_handler: ['void', ['pointer', 'pointer', 'pointer']], 58 | libvlc_wait: ['void', ['pointer']], 59 | libvlc_set_user_agent: ['void', ['pointer', 'string', 'string']], 60 | libvlc_get_version: ['string', []], 61 | libvlc_get_compiler: ['string', []], 62 | libvlc_get_changeset: ['string', []], 63 | libvlc_free: ['void', ['pointer']], 64 | libvlc_event_attach: ['int32', ['pointer', 'int32', 'pointer', ref.types.Object]], 65 | libvlc_event_detach: ['void', ['pointer', 'int32', 'pointer', ref.types.Object]], 66 | libvlc_event_type_name: ['string', ['int32']], 67 | libvlc_module_description_list_release: ['void', [ModuleDescriptionPtr]], 68 | libvlc_audio_filter_list_get: [ModuleDescriptionPtr, ['pointer']], 69 | libvlc_video_filter_list_get: [ModuleDescriptionPtr, ['pointer']], 70 | libvlc_clock: ['longlong', []], 71 | //libvlc_delay: ['longlong', ['longlong']], 72 | libvlc_media_new_location: ['pointer', ['pointer', 'string']], 73 | libvlc_media_new_path: ['pointer', ['pointer', 'string']], 74 | libvlc_media_new_fd: ['pointer', ['pointer', 'int32']], 75 | libvlc_media_new_as_node: ['pointer', ['pointer', 'string']], 76 | libvlc_media_add_option: ['void', ['pointer', 'string']], 77 | libvlc_media_add_option_flag: ['void', ['pointer', 'string', 'uint32']], 78 | libvlc_media_retain: ['void', ['pointer']], 79 | libvlc_media_release: ['void', ['pointer']], 80 | libvlc_media_get_mrl: ['string', ['pointer']], 81 | libvlc_media_duplicate: ['pointer', ['pointer']], 82 | libvlc_media_get_meta: ['string', ['pointer', 'uint32']], 83 | libvlc_media_set_meta: ['void', ['pointer', 'uint32', 'string']], 84 | libvlc_media_save_meta: ['int32', ['pointer']], 85 | libvlc_media_get_state: ['uint32', ['pointer']], 86 | libvlc_media_get_stats: ['int32', ['pointer', MediaStatsPtr]], 87 | libvlc_media_subitems: ['pointer', ['pointer']], 88 | libvlc_media_event_manager: ['pointer', ['pointer']], 89 | libvlc_media_get_duration: ['longlong', ['pointer']], 90 | libvlc_media_parse: ['void', ['pointer']], 91 | libvlc_media_parse_async: ['void', ['pointer']], 92 | libvlc_media_is_parsed: ['int32', ['pointer']], 93 | libvlc_media_set_user_data: ['void', ['pointer', 'pointer']], 94 | libvlc_media_get_user_data: ['pointer', ['pointer']], 95 | libvlc_media_get_tracks_info: ['int32', ['pointer', 'pointer']], 96 | libvlc_media_player_new: ['pointer', ['pointer']], 97 | libvlc_media_player_new_from_media: ['pointer', ['pointer']], 98 | libvlc_media_player_release: ['void', ['pointer']], 99 | libvlc_media_player_retain: ['void', ['pointer']], 100 | libvlc_media_player_set_media: ['void', ['pointer', 'pointer']], 101 | libvlc_media_player_get_media: ['pointer', ['pointer']], 102 | libvlc_media_player_event_manager: ['pointer', ['pointer']], 103 | libvlc_media_player_is_playing: ['int32', ['pointer']], 104 | libvlc_media_player_play: ['int32', ['pointer']], 105 | libvlc_media_player_set_pause: ['void', ['pointer', 'int32']], 106 | libvlc_media_player_pause: ['void', ['pointer']], 107 | libvlc_media_player_stop: ['void', ['pointer']], 108 | libvlc_video_set_callbacks: ['void', ['pointer', 'pointer', 'pointer', 'pointer', 'pointer']], 109 | libvlc_video_set_format: ['void', ['pointer', 'string', 'uint32', 'uint32', 'uint32']], 110 | libvlc_video_set_format_callbacks: ['void', ['pointer', 'pointer', 'pointer']], 111 | libvlc_media_player_set_nsobject: ['void', ['pointer', 'pointer']], 112 | libvlc_media_player_get_nsobject: ['pointer', ['pointer']], 113 | libvlc_media_player_set_agl: ['void', ['pointer', 'uint32']], 114 | libvlc_media_player_get_agl: ['uint32', ['pointer']], 115 | libvlc_media_player_set_xwindow: ['void', ['pointer', 'uint32']], 116 | libvlc_media_player_get_xwindow: ['uint32', ['pointer']], 117 | libvlc_media_player_set_hwnd: ['void', ['pointer', 'pointer']], 118 | libvlc_media_player_get_hwnd: ['pointer', ['pointer']], 119 | libvlc_audio_set_callbacks: ['void', ['pointer', 'pointer', 'pointer', 'pointer', 'pointer', 'pointer', 'pointer']], 120 | libvlc_audio_set_volume_callback: ['void', ['pointer', 'pointer']], 121 | libvlc_audio_set_format_callbacks: ['void', ['pointer', 'pointer', 'pointer']], 122 | libvlc_audio_set_format: ['void', ['pointer', 'string', 'uint32', 'uint32']], 123 | libvlc_media_player_get_length: ['longlong', ['pointer']], 124 | libvlc_media_player_get_time: ['longlong', ['pointer']], 125 | libvlc_media_player_set_time: ['void', ['pointer', 'longlong']], 126 | libvlc_media_player_get_position: ['float', ['pointer']], 127 | libvlc_media_player_set_position: ['void', ['pointer', 'float']], 128 | libvlc_media_player_set_chapter: ['void', ['pointer', 'int32']], 129 | libvlc_media_player_get_chapter: ['int32', ['pointer']], 130 | libvlc_media_player_get_chapter_count: ['int32', ['pointer']], 131 | libvlc_media_player_will_play: ['int32', ['pointer']], 132 | libvlc_media_player_get_chapter_count_for_title: ['int32', ['pointer', 'int32']], 133 | libvlc_media_player_set_title: ['void', ['pointer', 'int32']], 134 | libvlc_media_player_get_title: ['int32', ['pointer']], 135 | libvlc_media_player_get_title_count: ['int32', ['pointer']], 136 | libvlc_media_player_previous_chapter: ['void', ['pointer']], 137 | libvlc_media_player_next_chapter: ['void', ['pointer']], 138 | libvlc_media_player_get_rate: ['float', ['pointer']], 139 | libvlc_media_player_set_rate: ['int32', ['pointer', 'float']], 140 | libvlc_media_player_get_state: ['uint32', ['pointer']], 141 | libvlc_media_player_get_fps: ['float', ['pointer']], 142 | libvlc_media_player_has_vout: ['uint32', ['pointer']], 143 | libvlc_media_player_is_seekable: ['int32', ['pointer']], 144 | libvlc_media_player_can_pause: ['int32', ['pointer']], 145 | libvlc_media_player_next_frame: ['void', ['pointer']], 146 | libvlc_media_player_navigate: ['void', ['pointer', 'uint32']], 147 | libvlc_track_description_list_release: ['void', ['pointer']], 148 | libvlc_track_description_release: ['void', ['pointer']], 149 | libvlc_toggle_fullscreen: ['void', ['pointer']], 150 | libvlc_set_fullscreen: ['void', ['pointer', 'int32']], 151 | libvlc_get_fullscreen: ['int32', ['pointer']], 152 | libvlc_video_set_key_input: ['void', ['pointer', 'uint32']], 153 | libvlc_video_set_mouse_input: ['void', ['pointer', 'uint32']], 154 | libvlc_video_get_size: ['int32', ['pointer', 'uint32', 'pointer', 'pointer']], 155 | libvlc_video_get_height: ['int32', ['pointer']], 156 | libvlc_video_get_width: ['int32', ['pointer']], 157 | libvlc_video_get_cursor: ['int32', ['pointer', 'uint32', 'pointer', 'pointer']], 158 | libvlc_video_get_scale: ['float', ['pointer']], 159 | libvlc_video_set_scale: ['void', ['pointer', 'float']], 160 | libvlc_video_get_aspect_ratio: ['string', ['pointer']], 161 | libvlc_video_set_aspect_ratio: ['void', ['pointer', 'string']], 162 | libvlc_video_get_spu: ['int32', ['pointer']], 163 | libvlc_video_get_spu_count: ['int32', ['pointer']], 164 | libvlc_video_get_spu_description: ['pointer', ['pointer']], 165 | libvlc_video_set_spu: ['int32', ['pointer', 'uint32']], 166 | libvlc_video_set_subtitle_file: ['int32', ['pointer', 'string']], 167 | libvlc_video_get_spu_delay: ['longlong', ['pointer']], 168 | libvlc_video_set_spu_delay: ['int32', ['pointer', 'longlong']], 169 | libvlc_video_get_title_description: ['pointer', ['pointer']], 170 | libvlc_video_get_chapter_description: ['pointer', ['pointer', 'int32']], 171 | libvlc_video_get_crop_geometry: ['string', ['pointer']], 172 | libvlc_video_set_crop_geometry: ['void', ['pointer', 'string']], 173 | libvlc_video_get_teletext: ['int32', ['pointer']], 174 | libvlc_video_set_teletext: ['void', ['pointer', 'int32']], 175 | libvlc_toggle_teletext: ['void', ['pointer']], 176 | libvlc_video_get_track_count: ['int32', ['pointer']], 177 | libvlc_video_get_track_description: [TrackDescriptionPtr, ['pointer']], 178 | libvlc_video_get_track: ['int32', ['pointer']], 179 | libvlc_video_set_track: ['int32', ['pointer', 'int32']], 180 | libvlc_video_take_snapshot: ['int32', ['pointer', 'uint32', 'string', 'uint32', 'uint32']], 181 | libvlc_video_set_deinterlace: ['void', ['pointer', 'string']], 182 | libvlc_video_get_marquee_int: ['int32', ['pointer', 'uint32']], 183 | libvlc_video_get_marquee_string: ['string', ['pointer', 'uint32']], 184 | libvlc_video_set_marquee_int: ['void', ['pointer', 'uint32', 'int32']], 185 | libvlc_video_set_marquee_string: ['void', ['pointer', 'uint32', 'string']], 186 | libvlc_video_get_logo_int: ['int32', ['pointer', 'uint32']], 187 | libvlc_video_set_logo_int: ['void', ['pointer', 'uint32', 'int32']], 188 | libvlc_video_set_logo_string: ['void', ['pointer', 'uint32', 'string']], 189 | libvlc_video_get_adjust_int: ['int32', ['pointer', 'uint32']], 190 | libvlc_video_set_adjust_int: ['void', ['pointer', 'uint32', 'int32']], 191 | libvlc_video_get_adjust_float: ['float', ['pointer', 'uint32']], 192 | libvlc_video_set_adjust_float: ['void', ['pointer', 'uint32', 'float']], 193 | libvlc_audio_output_list_get: ['pointer', ['pointer']], 194 | libvlc_audio_output_list_release: ['void', ['pointer']], 195 | libvlc_audio_output_set: ['int32', ['pointer', 'string']], 196 | libvlc_audio_output_device_count: ['int32', ['pointer', 'string']], 197 | libvlc_audio_output_device_longname: ['string', ['pointer', 'string', 'int32']], 198 | libvlc_audio_output_device_id: ['string', ['pointer', 'string', 'int32']], 199 | libvlc_audio_output_device_set: ['void', ['pointer', 'string', 'string']], 200 | libvlc_audio_output_get_device_type: ['int32', ['pointer']], 201 | libvlc_audio_output_set_device_type: ['void', ['pointer', 'int32']], 202 | libvlc_audio_toggle_mute: ['void', ['pointer']], 203 | libvlc_audio_get_mute: ['int32', ['pointer']], 204 | libvlc_audio_set_mute: ['void', ['pointer', 'int32']], 205 | libvlc_audio_get_volume: ['int32', ['pointer']], 206 | libvlc_audio_set_volume: ['int32', ['pointer', 'int32']], 207 | libvlc_audio_get_track_count: ['int32', ['pointer']], 208 | libvlc_audio_get_track_description: [TrackDescriptionPtr, ['pointer']], 209 | libvlc_audio_get_track: ['int32', ['pointer']], 210 | libvlc_audio_set_track: ['int32', ['pointer', 'int32']], 211 | libvlc_audio_get_channel: ['int32', ['pointer']], 212 | libvlc_audio_set_channel: ['int32', ['pointer', 'int32']], 213 | libvlc_audio_get_delay: ['longlong', ['pointer']], 214 | libvlc_audio_set_delay: ['int32', ['pointer', 'longlong']], 215 | libvlc_media_list_new: ['pointer', ['pointer']], 216 | libvlc_media_list_release: ['void', ['pointer']], 217 | libvlc_media_list_retain: ['void', ['pointer']], 218 | //libvlc_media_list_add_file_content: ['int32', ['pointer', 'string']], 219 | libvlc_media_list_set_media: ['void', ['pointer', 'pointer']], 220 | libvlc_media_list_media: ['pointer', ['pointer']], 221 | libvlc_media_list_add_media: ['int32', ['pointer', 'pointer']], 222 | libvlc_media_list_insert_media: ['int32', ['pointer', 'pointer', 'int32']], 223 | libvlc_media_list_remove_index: ['int32', ['pointer', 'int32']], 224 | libvlc_media_list_count: ['int32', ['pointer']], 225 | libvlc_media_list_item_at_index: ['pointer', ['pointer', 'int32']], 226 | libvlc_media_list_index_of_item: ['int32', ['pointer', 'pointer']], 227 | libvlc_media_list_is_readonly: ['int32', ['pointer']], 228 | libvlc_media_list_lock: ['void', ['pointer']], 229 | libvlc_media_list_unlock: ['void', ['pointer']], 230 | libvlc_media_list_event_manager: ['pointer', ['pointer']], 231 | libvlc_media_list_player_new: ['pointer', ['pointer']], 232 | libvlc_media_list_player_release: ['void', ['pointer']], 233 | libvlc_media_list_player_retain: ['void', ['pointer']], 234 | libvlc_media_list_player_event_manager: ['pointer', ['pointer']], 235 | libvlc_media_list_player_set_media_player: ['void', ['pointer', 'pointer']], 236 | libvlc_media_list_player_set_media_list: ['void', ['pointer', 'pointer']], 237 | libvlc_media_list_player_play: ['void', ['pointer']], 238 | libvlc_media_list_player_pause: ['void', ['pointer']], 239 | libvlc_media_list_player_is_playing: ['int32', ['pointer']], 240 | libvlc_media_list_player_get_state: ['uint32', ['pointer']], 241 | libvlc_media_list_player_play_item_at_index: ['int32', ['pointer', 'int32']], 242 | libvlc_media_list_player_play_item: ['int32', ['pointer', 'pointer']], 243 | libvlc_media_list_player_stop: ['void', ['pointer']], 244 | libvlc_media_list_player_next: ['int32', ['pointer']], 245 | libvlc_media_list_player_previous: ['int32', ['pointer']], 246 | libvlc_media_list_player_set_playback_mode: ['void', ['pointer', 'uint32']], 247 | libvlc_media_library_new: ['pointer', ['pointer']], 248 | libvlc_media_library_release: ['void', ['pointer']], 249 | libvlc_media_library_retain: ['void', ['pointer']], 250 | libvlc_media_library_load: ['int32', ['pointer']], 251 | libvlc_media_library_media_list: ['pointer', ['pointer']], 252 | libvlc_media_discoverer_new_from_name: ['pointer', ['pointer', 'string']], 253 | libvlc_media_discoverer_release: ['void', ['pointer']], 254 | libvlc_media_discoverer_localized_name: ['string', ['pointer']], 255 | libvlc_media_discoverer_media_list: ['pointer', ['pointer']], 256 | libvlc_media_discoverer_event_manager: ['pointer', ['pointer']], 257 | libvlc_media_discoverer_is_running: ['int32', ['pointer']], 258 | libvlc_vlm_release: ['void', ['pointer']], 259 | libvlc_vlm_add_broadcast: ['int32', ['pointer', 'string', 'string', 'string', 'int32', 'pointer', 'int32', 'int32']], 260 | libvlc_vlm_add_vod: ['int32', ['pointer', 'string', 'string', 'int32', 'pointer', 'int32', 'string']], 261 | libvlc_vlm_del_media: ['int32', ['pointer', 'string']], 262 | libvlc_vlm_set_enabled: ['int32', ['pointer', 'string', 'int32']], 263 | libvlc_vlm_set_output: ['int32', ['pointer', 'string', 'string']], 264 | libvlc_vlm_set_input: ['int32', ['pointer', 'string', 'string']], 265 | libvlc_vlm_add_input: ['int32', ['pointer', 'string', 'string']], 266 | libvlc_vlm_set_loop: ['int32', ['pointer', 'string', 'int32']], 267 | libvlc_vlm_set_mux: ['int32', ['pointer', 'string', 'string']], 268 | libvlc_vlm_change_media: ['int32', ['pointer', 'string', 'string', 'string', 'int32', 'pointer', 'int32', 'int32']], 269 | libvlc_vlm_play_media: ['int32', ['pointer', 'string']], 270 | libvlc_vlm_stop_media: ['int32', ['pointer', 'string']], 271 | libvlc_vlm_pause_media: ['int32', ['pointer', 'string']], 272 | libvlc_vlm_seek_media: ['int32', ['pointer', 'string', 'float']], 273 | libvlc_vlm_show_media: ['string', ['pointer', 'string']], 274 | libvlc_vlm_get_media_instance_position: ['float', ['pointer', 'string', 'int32']], 275 | libvlc_vlm_get_media_instance_time: ['int32', ['pointer', 'string', 'int32']], 276 | libvlc_vlm_get_media_instance_length: ['int32', ['pointer', 'string', 'int32']], 277 | libvlc_vlm_get_media_instance_rate: ['int32', ['pointer', 'string', 'int32']], 278 | libvlc_vlm_get_event_manager: ['pointer', ['pointer']], 279 | libvlc_playlist_play: ['void', ['pointer', 'int32', 'int32', 'pointer']], 280 | }); 281 | 282 | return module.exports; 283 | }; 284 | -------------------------------------------------------------------------------- /lib/libvlc_types.js: -------------------------------------------------------------------------------- 1 | var ref = require('ref'); 2 | var Struct = require('ref-struct'); 3 | var Union = require('ref-union'); 4 | 5 | exports.TrackInfo = Struct({ 6 | i_codec: ref.types.uint32, 7 | i_id: ref.types.int32, 8 | i_type: ref.types.uint32, 9 | i_profile: ref.types.int32, 10 | i_level: ref.types.int32, 11 | union1: ref.types.uint32, 12 | union2: ref.types.uint32, 13 | }); 14 | 15 | exports.MediaStats = Struct({ 16 | i_read_bytes: ref.types.int32, 17 | f_input_bitrate: ref.types.float, 18 | i_demux_read_bytes: ref.types.int32, 19 | f_demux_bitrate: ref.types.float, 20 | i_demux_corrupted: ref.types.int32, 21 | i_demux_discontinuity: ref.types.int32, 22 | i_decoded_video: ref.types.int32, 23 | i_decoded_audio: ref.types.int32, 24 | i_displayed_pictures: ref.types.int32, 25 | i_lost_pictures: ref.types.int32, 26 | i_played_abuffers: ref.types.int32, 27 | i_lost_abuffers: ref.types.int32, 28 | i_sent_packets: ref.types.int32, 29 | i_sent_bytes: ref.types.int32, 30 | f_send_bitrate: ref.types.float, 31 | }); 32 | exports.MediaStatsPtr = ref.refType(exports.MediaStats); 33 | 34 | var TrackDescription = exports.TrackDescription = Struct({ 35 | i_id: ref.types.int32, 36 | psz_name: ref.types.CString, 37 | }); 38 | var TrackDescriptionPtr = exports.TrackDescriptionPtr = ref.refType(TrackDescription); 39 | TrackDescription.defineProperty('p_next', TrackDescriptionPtr); 40 | 41 | exports.ModuleDescription = Struct({ 42 | psz_name: ref.types.CString, 43 | psz_shortname: ref.types.CString, 44 | psz_longname: ref.types.CString, 45 | psz_help: ref.types.CString, 46 | }); 47 | exports.ModuleDescriptionPtr = ref.refType(exports.ModuleDescription); 48 | exports.ModuleDescription.defineProperty('p_next', exports.ModuleDescriptionPtr); 49 | 50 | var EventUnion = Union({ 51 | media_meta_changed: Struct({ meta_type: ref.types.int32 }), 52 | media_subitem_added: Struct({ new_child: ref.refType(ref.types.void) }), 53 | media_duration_changed: Struct({ new_duration: ref.types.int64 }), 54 | media_parsed_changed: Struct({ new_status: ref.types.int32 }), 55 | media_freed: Struct({ md: ref.refType(ref.types.void) }), 56 | media_state_changed: Struct({ new_state: ref.types.uint32 }), 57 | media_player_buffering: Struct({ new_cache: ref.types.float }), 58 | media_player_position_changed: Struct({ new_position: ref.types.float }), 59 | media_player_time_changed: Struct({ new_time: ref.types.int64 }), 60 | media_player_title_changed: Struct({ new_title: ref.types.int32 }), 61 | media_player_seekable_changed: Struct({ new_seekable: ref.types.int32 }), 62 | media_player_pausable_changed: Struct({ new_pausable: ref.types.int32 }), 63 | media_player_vout: Struct({ new_count: ref.types.int32 }), 64 | }); 65 | 66 | exports.Event = Struct({ 67 | type: ref.types.int32, 68 | p_obj: ref.refType(ref.types.void), 69 | u: EventUnion, 70 | }); 71 | 72 | exports.EventPtr = ref.refType(exports.Event); 73 | -------------------------------------------------------------------------------- /lib/media.js: -------------------------------------------------------------------------------- 1 | var ref = require('ref'); 2 | 3 | var lib = require('./libvlc'); 4 | var util = require('./util'); 5 | 6 | var TrackInfo = require('./libvlc_types').TrackInfo; 7 | var MediaStats = require('./libvlc_types').MediaStats; 8 | var EventPtr = require('./libvlc_types').EventPtr; 9 | 10 | var metaName = require('./media_enum').metaName; 11 | var metaEnum = require('./media_enum').metaEnum; 12 | var state = require('./media_enum').state; 13 | 14 | var EventEmitter = require('events').EventEmitter; 15 | 16 | var trackType = { 17 | unknown: -1, 18 | '-1': 'unknown', 19 | audio: 0, 20 | '0': 'audio', 21 | video: 1, 22 | '1': 'video', 23 | text: 2, 24 | '2': 'text', 25 | } 26 | 27 | var ffi = require('ffi') 28 | var libc = ffi.Library(null, { 29 | 'free': ['void', [ 'pointer' ]], 30 | }); 31 | 32 | var VALID_EVENTS = { 33 | MetaChanged: 0, 34 | SubItemAdded: 1, 35 | DurationChanged: 2, 36 | ParsedChanged: 3, 37 | Freed: 4, 38 | StateChanged: 5, 39 | 0: 'MetaChanged', 40 | 1: 'SubItemAdded', 41 | 2: 'DurationChanged', 42 | 3: 'ParsedChanged', 43 | 4: 'Freed', 44 | 5: 'StateChanged', 45 | }; 46 | 47 | var event_handler = function(e, d) { 48 | var event = e.deref(); 49 | var obj = d.deref(); 50 | var union = event.u; 51 | var name, arg; 52 | 53 | if (obj.instance.address() != event.p_obj.address()) 54 | return; 55 | 56 | switch (event.type) { 57 | case 0: 58 | name = 'MetaChanged'; 59 | arg = metaEnum[union.media_meta_changed.meta_type]; 60 | break; 61 | case 1: 62 | name = 'SubItemAdded'; 63 | arg = new Media(obj.parent, union.media_subitem_added.new_child); 64 | break; 65 | case 2: 66 | name = 'DurationChanged'; 67 | arg = union.media_duration_changed.new_duration; 68 | break; 69 | case 3: 70 | name = 'ParsedChanged'; 71 | arg = union.media_parsed_changed.new_status; 72 | break; 73 | case 4: 74 | name = 'Freed'; 75 | arg = new Media(obj.parent, union.media_freed.md); 76 | break; 77 | case 5: 78 | name = 'StateChanged'; 79 | arg = state[union.media_state_changed.new_state]; 80 | break; 81 | } 82 | 83 | if (name) { 84 | obj.emit(name, arg); 85 | } 86 | }; 87 | 88 | var eventcb = ffi.Callback(ref.types.void, [ 89 | EventPtr, 90 | ref.refType(ref.types.Object) 91 | ], event_handler); 92 | 93 | var Media = module.exports = function (parent, reinit, opts) { 94 | var self = this; 95 | var released = false; 96 | var eventmgr; 97 | this.parent = parent; 98 | 99 | var selfref = ref.alloc(ref.types.Object, self); 100 | 101 | if (reinit) { 102 | self.instance = reinit; 103 | } else if (opts.path) { 104 | self.instance = lib.libvlc_media_new_path(parent, opts.path); 105 | } else if (opts.url) { 106 | self.instance = lib.libvlc_media_new_location(parent, opts.url); 107 | } else if (opts.fd) { 108 | self.instance = lib.libvlc_media_new_fd(parent, opts.fd); 109 | } else { 110 | self.instance = lib.libvlc_media_new_as_node(parent); 111 | } 112 | 113 | this.release = function () { 114 | if (!released) { 115 | lib.libvlc_media_release(self.instance); 116 | released = true; 117 | } 118 | }; 119 | 120 | this.parseSync = function () { 121 | lib.libvlc_media_parse(self.instance); 122 | }; 123 | 124 | this.saveSync = function () { 125 | lib.libvlc_media_save_meta(self.instance); 126 | }; 127 | 128 | Object.defineProperty(this, 'mrl', { 129 | get: function () { 130 | return lib.libvlc_media_get_mrl(self.instance); 131 | }, 132 | }); 133 | 134 | Object.defineProperty(this, 'is_parsed', { 135 | get: function () { 136 | return lib.libvlc_media_is_parsed(self.instance); 137 | }, 138 | }); 139 | 140 | Object.defineProperty(this, 'duration', { 141 | get: function () { 142 | return lib.libvlc_media_get_duration(self.instance); 143 | }, 144 | }); 145 | 146 | Object.defineProperty(this, 'track_info', { 147 | get: function () { 148 | var i, ptr, tmp; 149 | var results = []; 150 | 151 | var info = new Buffer(ref.sizeof.pointer); 152 | var tracks = lib.libvlc_media_get_tracks_info(self.instance, info); 153 | info = info.readPointer(0, TrackInfo.size * tracks); 154 | 155 | for (i = 0; i < tracks; i++) { 156 | ptr = ref.get(info, i * TrackInfo.size, TrackInfo); 157 | 158 | tmp = { 159 | codec: ptr.i_codec, 160 | id: ptr.i_id, 161 | type: trackType[ptr.i_type], 162 | profile: ptr.i_profile, 163 | level: ptr.i_level, 164 | }; 165 | 166 | switch (tmp.type) { 167 | case 'audio': 168 | tmp.channels = ptr.union1; 169 | tmp.rate = ptr.union2; 170 | break; 171 | case 'video': 172 | tmp.height = ptr.union1; 173 | tmp.width = ptr.union2; 174 | break; 175 | } 176 | 177 | results.push(tmp); 178 | } 179 | 180 | libc.free(info); 181 | info = undefined; 182 | 183 | return results; 184 | }, 185 | }); 186 | 187 | Object.defineProperty(this, 'stats', { 188 | get: function () { 189 | var ret, stats, tmp; 190 | stats = ref.alloc(MediaStats); 191 | ret = lib.libvlc_media_get_stats(self.instance, stats); 192 | stats = stats.deref(); 193 | tmp = { 194 | read_bytes: stats.i_read_bytes, 195 | input_bitrate: stats.f_input_bitrate, 196 | demux_read_bytes: stats.i_demux_read_bytes, 197 | demux_bitrate: stats.f_demux_bitrate, 198 | demux_corrupted: stats.i_demux_corrupted, 199 | demux_discontinuity: stats.i_demux_discontinuity, 200 | decoded_video: stats.i_decoded_video, 201 | decoded_audio: stats.i_decoded_audio, 202 | displayed_pictures: stats.i_displayed_pictures, 203 | lost_pictures: stats.i_lost_pictures, 204 | played_abuffers: stats.i_played_abuffers, 205 | lost_abuffers: stats.i_lost_abuffers, 206 | sent_packets: stats.i_sent_packets, 207 | sent_bytes: stats.i_sent_bytes, 208 | send_bitrate: stats.f_send_bitrate, 209 | }; 210 | return ret === 0 ? util.throw() : tmp; 211 | }, 212 | }); 213 | 214 | Object.keys(metaName).forEach(function (meta) { 215 | Object.defineProperty(self, meta, { 216 | get: function () { 217 | return lib.libvlc_media_get_meta(self.instance, metaName[meta]); 218 | }, 219 | set: function (val) { 220 | lib.libvlc_media_set_meta(self.instance, metaName[meta], val); 221 | }, 222 | enumerable: true, 223 | configurable: true, 224 | }); 225 | }); 226 | 227 | var _on = this.on; 228 | this.on = function (e, cb) { 229 | var tmp; 230 | 231 | if (!eventmgr) { 232 | eventmgr = lib.libvlc_media_event_manager(self.instance); 233 | } 234 | if (VALID_EVENTS[e] !== undefined) { 235 | if (!self._events || !self._events[e]) { 236 | tmp = lib.libvlc_event_attach(eventmgr, VALID_EVENTS[e], eventcb, selfref); 237 | if (tmp != 0) util.throw(); 238 | } 239 | _on.call(self, e, cb); 240 | } else { 241 | throw new Error("Not a valid event type: " + e + " not in " + Object.keys(VALID_EVENTS)); 242 | } 243 | }; 244 | 245 | var _rl = this.removeListener; 246 | this.removeListener = function (e, cb) { 247 | if (!eventmgr) 248 | return; 249 | if (VALID_EVENTS[e] !== undefined) { 250 | if (self._events && ((self._events[e] instanceof Function) || self._events[e].length === 1)) { 251 | lib.libvlc_event_detach(eventmgr, VALID_EVENTS[e], eventcb, selfref); 252 | } 253 | _rl.call(self, e, cb); 254 | } else { 255 | throw new Error("Not a valid event type: " + e + " not in " + Object.keys(VALID_EVENTS)); 256 | } 257 | }; 258 | }; 259 | require('util').inherits(Media, EventEmitter); 260 | -------------------------------------------------------------------------------- /lib/media_enum.js: -------------------------------------------------------------------------------- 1 | var metaEnum = [ 2 | 'title', 3 | 'artist', 4 | 'genre', 5 | 'copyright', 6 | 'album', 7 | 'tracknumber', 8 | 'description', 9 | 'rating', 10 | 'date', 11 | 'setting', 12 | 'url', 13 | 'language', 14 | 'nowplaying', 15 | 'publisher', 16 | 'encodedby', 17 | 'artworkurl', 18 | 'trackid', 19 | ]; 20 | 21 | var metaName = {}; 22 | 23 | metaEnum.forEach(function (meta, index) { 24 | metaName[meta] = index; 25 | }); 26 | 27 | exports.metaName = metaName; 28 | exports.metaEnum = metaEnum; 29 | 30 | exports.state = { 31 | 0: 'NothingSpecial', 32 | 1: 'Opening', 33 | 2: 'Buffering', 34 | 3: 'Playing', 35 | 4: 'Paused', 36 | 5: 'Stopped', 37 | 6: 'Ended', 38 | 7: 'Error', 39 | NothingSpecial: 0, 40 | Opening: 1, 41 | Buffering: 2, 42 | Playing: 3, 43 | Paused: 4, 44 | Stopped: 5, 45 | Ended: 6, 46 | Error: 7 47 | }; 48 | -------------------------------------------------------------------------------- /lib/mediaplayer.js: -------------------------------------------------------------------------------- 1 | var EventEmitter = require('events').EventEmitter; 2 | 3 | var ref = require('ref'); 4 | var ffi = require('ffi'); 5 | 6 | var lib = require('./libvlc'); 7 | var EventPtr = require('./libvlc_types').EventPtr; 8 | var util = require('./util'); 9 | 10 | var Media = require('./media'); 11 | var Video = require('./video'); 12 | var Audio = require('./audio'); 13 | 14 | var properties = { 15 | is_playing: { }, 16 | is_seekable: { }, 17 | will_play: { }, 18 | can_pause: { }, 19 | state: { get: true, }, 20 | length: { get: true, }, 21 | fps: { get: true, }, 22 | chapter_count: { get: true, }, 23 | title_count: { get: true, }, 24 | time: { get: true, set: true, }, 25 | position: { get: true, set: true, }, 26 | chapter: { get: true, set: true, }, 27 | title: { get: true, set: true, }, 28 | rate: { get: true, set: true, }, 29 | }; 30 | 31 | var VALID_EVENTS = { 32 | 0x100: 'MediaChanged', 33 | 0x101: 'NothingSpecial', 34 | 0x102: 'Opening', 35 | 0x103: 'Buffering', 36 | 0x104: 'Playing', 37 | 0x105: 'Paused', 38 | 0x106: 'Stopped', 39 | 0x107: 'Forward', 40 | 0x108: 'Backward', 41 | 0x109: 'EndReached', 42 | 0x10A: 'EncounteredError', 43 | 0x10B: 'TimeChanged', 44 | 0x10C: 'PositionChanged', 45 | 0x10D: 'SeekableChanged', 46 | 0x10E: 'PausableChanged', 47 | 0x10F: 'TitleChanged', 48 | 0x110: 'SnapshotTaken', 49 | 0x111: 'LengthChanged', 50 | 0x112: 'Vout', 51 | MediaChanged: 0x100, 52 | NothingSpecial: 0x101, 53 | Opening: 0x102, 54 | Buffering: 0x103, 55 | Playing: 0x104, 56 | Paused: 0x105, 57 | Stopped: 0x106, 58 | Forward: 0x107, 59 | Backward: 0x108, 60 | EndReached: 0x109, 61 | EncounteredError: 0x10A, 62 | TimeChanged: 0x10B, 63 | PositionChanged: 0x10C, 64 | SeekableChanged: 0x10D, 65 | PausableChanged: 0x10E, 66 | TitleChanged: 0x10F, 67 | SnapshotTaken: 0x110, 68 | LengthChanged: 0x111, 69 | Vout: 0x112, 70 | }; 71 | 72 | var event_handler = function(e, d) { 73 | var event = e.deref(); 74 | var obj = d.deref(); 75 | var union = event.u; 76 | var name, arg; 77 | 78 | if (obj.instance.address() != event.p_obj.address()) 79 | return; 80 | 81 | name = VALID_EVENTS[event.type]; 82 | 83 | if (!name) 84 | return; 85 | 86 | switch (event.type) { 87 | case 0x103: 88 | arg = union.media_player_buffering.new_cache; 89 | break; 90 | case 0x10B: 91 | arg = union.media_player_time_changed.new_time; 92 | break; 93 | case 0x10C: 94 | arg = union.media_player_position_changed.new_position; 95 | break; 96 | case 0x10D: 97 | arg = union.media_player_seekable_changed.new_seekable; 98 | break; 99 | case 0x10E: 100 | arg = union.media_player_pausable_changed.new_pausable; 101 | break; 102 | case 0x10F: 103 | arg = union.media_player_title_changed.new_title; 104 | break; 105 | case 0x112: 106 | arg = union.media_player_vout.new_count; 107 | break; 108 | }; 109 | 110 | obj.emit(name, arg); 111 | }; 112 | 113 | var eventcb = ffi.Callback(ref.types.void, [ 114 | EventPtr, 115 | ref.refType(ref.types.Object) 116 | ], event_handler); 117 | 118 | var MediaPlayer = module.exports = function (parent) { 119 | var self = this; 120 | var released = false; 121 | var video, audio, eventmgr; 122 | 123 | var selfref = ref.alloc(ref.types.Object, self); 124 | 125 | this.parent = parent; 126 | this.instance = lib.libvlc_media_player_new(parent); 127 | 128 | this.release = function () { 129 | if (!released) { 130 | lib.libvlc_media_player_release(self.instance); 131 | released = true; 132 | } 133 | }; 134 | 135 | util.makeProperties(self, self.instance, properties, 'libvlc_media_player_'); 136 | 137 | Object.defineProperty(this, 'media', { 138 | get: function () { 139 | var mi = lib.libvlc_media_player_get_media(self.instance); 140 | return new Media(self.parent, mi); 141 | }, 142 | set: function (val) { 143 | lib.libvlc_media_player_set_media(self.instance, val.instance); 144 | }, 145 | }); 146 | 147 | Object.defineProperty(this, 'video', { 148 | get: function () { 149 | if (!video) { 150 | video = new Video(self.instance); 151 | } 152 | return video; 153 | }, 154 | }); 155 | 156 | Object.defineProperty(this, 'audio', { 157 | get: function () { 158 | if (!audio) { 159 | audio = new Audio(self.instance); 160 | } 161 | return audio; 162 | }, 163 | }); 164 | 165 | this.play = function () { 166 | return lib.libvlc_media_player_play(self.instance) < 0 ? util.throw() : true; 167 | }; 168 | 169 | this.pause = function () { 170 | lib.libvlc_media_player_pause(self.instance); 171 | }; 172 | 173 | this.stop = function () { 174 | lib.libvlc_media_player_stop(self.instance); 175 | }; 176 | 177 | var _on = this.on; 178 | this.on = function (e, cb) { 179 | var tmp; 180 | 181 | if (!eventmgr) { 182 | eventmgr = lib.libvlc_media_player_event_manager(self.instance); 183 | } 184 | if (VALID_EVENTS[e] !== undefined) { 185 | if (!self._events || !self._events[e]) { 186 | tmp = lib.libvlc_event_attach(eventmgr, VALID_EVENTS[e], eventcb, selfref); 187 | if (tmp != 0) util.throw(); 188 | } 189 | _on.call(self, e, cb); 190 | } else { 191 | throw new Error("Not a valid event type: " + e + " not in " + Object.keys(VALID_EVENTS)); 192 | } 193 | }; 194 | 195 | var _rl = this.removeListener; 196 | this.removeListener = function (e, cb) { 197 | if (!eventmgr) 198 | return; 199 | if (VALID_EVENTS[e] !== undefined) { 200 | if (self._events && ((self._events[e] instanceof Function) || self._events[e].length === 1)) { 201 | lib.libvlc_event_detach(eventmgr, VALID_EVENTS[e], eventcb, selfref); 202 | } 203 | _rl.call(self, e, cb); 204 | } else { 205 | throw new Error("Not a valid event type: " + e + " not in " + Object.keys(VALID_EVENTS)); 206 | } 207 | }; 208 | }; 209 | require('util').inherits(MediaPlayer, EventEmitter); 210 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | var lib = require('./libvlc'); 2 | exports.throw = function () { 3 | var err = new Error(lib.libvlc_errmsg()); 4 | throw err; 5 | }; 6 | 7 | exports.makeProperties = function (obj, instance, properties, prefix) { 8 | var props = []; 9 | 10 | Object.keys(properties).forEach(function (prop) { 11 | var getter = prefix; 12 | var setter; 13 | 14 | if (properties[prop].get) { 15 | getter += 'get_'; 16 | } 17 | 18 | getter += prop; 19 | 20 | if (properties[prop].set) { 21 | setter = prefix + 'set_' + prop; 22 | } 23 | 24 | props.push({ 25 | name: prop, 26 | get: getter, 27 | set: setter, 28 | }); 29 | }); 30 | 31 | props.forEach(function (prop) { 32 | var get, set; 33 | 34 | get = function () { 35 | return lib[prop.get](instance); 36 | }; 37 | 38 | if (prop.set) { 39 | set = function (val) { 40 | return lib[prop.set](instance, val); 41 | }; 42 | } 43 | 44 | Object.defineProperty(obj, prop.name, { 45 | get: get, 46 | set: set, 47 | }); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /lib/video.js: -------------------------------------------------------------------------------- 1 | var lib = require('./libvlc'); 2 | 3 | var util = require('./util'); 4 | 5 | var properties = { 6 | fullscreen: { get: true, set: true }, 7 | size: { get: true }, 8 | height: { get: true }, 9 | width: { get: true }, 10 | cursor: { get: true }, 11 | scale: { get: true, set: true }, 12 | aspect_ratio: { get: true, set: true }, 13 | spu: { get: true, set: true }, 14 | spu_count: { get: true }, 15 | spu_description: { get: true }, 16 | spu_delay: { get: true, set: true }, 17 | crop_geometry: { get: true, set: true }, 18 | teletext: { get: true, set: true }, 19 | track_count: { get: true }, 20 | track: { get: true, set: true }, 21 | }; 22 | 23 | var Video = function (mediaplayer) { 24 | util.makeProperties(this, mediaplayer, properties, 'libvlc_video_'); 25 | 26 | this.take_snapshot = function (track, file, width, height) { 27 | var ret = lib.libvlc_video_take_snapshot(mediaplayer, track, file, width, height); 28 | return ret === 0 ? true : util.throw(); 29 | }; 30 | 31 | Object.defineProperty(this, 'description', { 32 | get: function () { 33 | var ret = [], tmp, start; 34 | 35 | start = tmp = lib.libvlc_video_get_track_description(mediaplayer); 36 | 37 | while(!tmp.isNull()) { 38 | tmp = tmp.deref(); 39 | ret.push({ 40 | id: tmp.i_id, 41 | name: tmp.psz_name, 42 | }); 43 | tmp = tmp.p_next; 44 | } 45 | 46 | if (!start.isNull()) 47 | lib.libvlc_track_description_list_release(start); 48 | 49 | return ret; 50 | }, 51 | }); 52 | }; 53 | 54 | module.exports = Video; 55 | -------------------------------------------------------------------------------- /lib/vlm.js: -------------------------------------------------------------------------------- 1 | var lib = require('./libvlc'); 2 | var util = require('./util'); 3 | 4 | var VLM = module.exports = function (parent) { 5 | var self = this; 6 | var released = false; 7 | 8 | self.parent = parent; 9 | 10 | this.release = function () { 11 | if (!released) { 12 | lib.libvlc_vlm_release(parent); 13 | released = true; 14 | } 15 | }; 16 | 17 | this.addBroadcast = function (name, mrl, sout, opts, enabled, loop) { 18 | return lib.libvlc_vlm_add_broadcast(parent, name, mrl, sout, 0, null, enabled ? 1 : 0, loop ? 1 : 0) === 0 ? true : util.throw(); 19 | } 20 | 21 | this.setMedia = function (name, mrl) { 22 | return lib.libvlc_vlm_set_input(parent, name, mrl) === 0 ? true : util.throw(); 23 | }; 24 | 25 | this.changeMedia = function (name, mrl, sout, opts, enabled, loop) { 26 | return lib.libvlc_vlm_change_media(parent, name, mrl, sout, 0, null, enabled ? 1 : 0, loop ? 1 : 0) === 0 ? true : util.throw(); 27 | } 28 | 29 | this.setEnabled = function (name, enabled) { 30 | return lib.libvlc_vlm_set_enabled(parent, name, enabled) === 0 ? true : util.throw(); 31 | }; 32 | 33 | this.setOutput = function (name, sout) { 34 | return lib.libvlc_vlm_set_output(parent, name, sout) === 0 ? true : util.throw(); 35 | }; 36 | 37 | this.showMedia = function (name) { 38 | return lib.libvlc_vlm_show_media(parent, name); 39 | }; 40 | 41 | this.stopMedia = function (name) { 42 | return lib.libvlc_vlm_stop_media(parent, name) === 0 ? true : util.throw(); 43 | }; 44 | 45 | this.playMedia = function (name) { 46 | return lib.libvlc_vlm_play_media(parent, name) === 0 ? true : util.throw(); 47 | }; 48 | 49 | this.pauseMedia = function (name) { 50 | return lib.libvlc_vlm_pause_media(parent, name) === 0 ? true : util.throw(); 51 | }; 52 | }; 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vlc", 3 | "version": "0.0.4", 4 | "author": "Timothy J Fontaine (http://atxconsulting.com)", 5 | "description": "VLC FFI Bindings", 6 | "keywords": [ "vlc" ], 7 | "homepage": "http://github.com/tjfontaine/node-vlc", 8 | "bugs": { 9 | "url": "http://github.com/tjfontaine/node-vlc/issues" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "http://github.com/tjfontaine/node-vlc.git" 14 | }, 15 | "main": "vlc.js", 16 | "engines": { 17 | "node": ">= 0.8.0" 18 | }, 19 | "dependencies": { 20 | "ffi": ">= 1.0.7", 21 | "ref": ">= 0.0.17", 22 | "ref-struct": ">= 0.0.3", 23 | "ref-union": ">= 0.0.2", 24 | "semver": ">= 1.0.14" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vlc.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | var path = require('path'); 3 | 4 | var FFI = require('ffi'); 5 | var ref = require('ref'); 6 | 7 | var LIBRARY_PATHS = []; 8 | 9 | switch (os.platform()) { 10 | case 'darwin': 11 | LIBRARY_PATHS.push('/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib'); 12 | LIBRARY_PATHS.push(path.join(process.env.HOME, 'Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib')); 13 | break; 14 | case 'win32': 15 | if (process.env['ProgramFiles(x86)']) { 16 | LIBRARY_PATHS.push(path.join('', 17 | process.env['ProgramFiles(x86)'], 18 | 'VideoLAN', 19 | 'VLC', 20 | 'libvlc.dll' 21 | )); 22 | } 23 | LIBRARY_PATHS.push(path.join( 24 | process.env['ProgramFiles'], 25 | 'VideoLAN', 26 | 'VLC', 27 | 'libvlc.dll' 28 | )); 29 | break; 30 | default: 31 | LIBRARY_PATHS.push('/usr/lib/libvlc.so'); 32 | LIBRARY_PATHS.push('/usr/lib/libvlc.so.5'); 33 | break; 34 | } 35 | 36 | exports.LIBRARY_PATHS = LIBRARY_PATHS; 37 | 38 | var lib, MediaPlayer, Media, VLM; 39 | 40 | var VLC = function (args) { 41 | if (!(this instanceof VLC)) { 42 | return new VLC(args); 43 | } 44 | 45 | if (!lib) { 46 | lib = require('./lib/libvlc').initialize(LIBRARY_PATHS); 47 | MediaPlayer = require('./lib/mediaplayer'); 48 | Media = require('./lib/media'); 49 | VLM = require('./lib/vlm'); 50 | } 51 | 52 | var mediaplayer, vlm; 53 | var released = false; 54 | 55 | var cargs = new Buffer(ref.sizeof.pointer * args.length); 56 | for (var i = 0; i < args.length; i++) { 57 | cargs.writePointer(ref.allocCString(args[i]), i * ref.sizeof.pointer); 58 | } 59 | 60 | instance = lib.libvlc_new(args.length, cargs); 61 | 62 | Object.defineProperty(this, 'mediaplayer', { 63 | get: function () { 64 | if (!mediaplayer) { 65 | mediaplayer = new MediaPlayer(instance); 66 | } 67 | return mediaplayer; 68 | }, 69 | }); 70 | 71 | Object.defineProperty(this, 'vlm', { 72 | get: function () { 73 | if (!vlm) { 74 | vlm = new VLM(instance); 75 | } 76 | return vlm; 77 | }, 78 | }); 79 | 80 | this.release = function () { 81 | if (!released) { 82 | if (mediaplayer) { 83 | mediaplayer.release(); 84 | } 85 | if (vlm) { 86 | vlm.release(); 87 | } 88 | lib.libvlc_release(instance); 89 | released = true; 90 | } 91 | }; 92 | 93 | this.mediaFromFile = function (path) { 94 | return new Media(instance, undefined, { path: path }); 95 | }; 96 | 97 | this.mediaFromUrl = function(url){ 98 | return new Media(instance, undefined, { url: url }); 99 | }; 100 | 101 | this.mediaFromFd = function(fd){ 102 | return new Media(instance, undefined, { fd: fd }); 103 | }; 104 | 105 | this.mediaFromNode = function(node){ 106 | return new Media(instance, undefined, node); 107 | }; 108 | 109 | this.mediaFields = require('./lib/media_enum').metaEnum; 110 | 111 | Object.defineProperty(this, 'audio_filters', { 112 | get: function() { 113 | var ret = [], tmp, start; 114 | start = tmp = lib.libvlc_audio_filter_list_get(instance); 115 | 116 | while (!tmp.isNull()) { 117 | tmp = tmp.deref(); 118 | ret.push({ 119 | name: tmp.psz_name, 120 | shortname: tmp.psz_shortname, 121 | longname: tmp.psz_longname, 122 | help: tmp.psz_help, 123 | }); 124 | tmp = tmp.p_next; 125 | } 126 | 127 | if (!start.isNull()) 128 | lib.libvlc_module_description_list_release(start); 129 | 130 | return ret; 131 | }, 132 | }); 133 | 134 | Object.defineProperty(this, 'video_filters', { 135 | get: function() { 136 | var ret = [], tmp, start; 137 | start = tmp = lib.libvlc_video_filter_list_get(instance); 138 | 139 | while (!tmp.isNull()) { 140 | tmp = tmp.deref(); 141 | ret.push({ 142 | name: tmp.psz_name, 143 | shortname: tmp.psz_shortname, 144 | longname: tmp.psz_longname, 145 | help: tmp.psz_help, 146 | }); 147 | tmp = tmp.p_next; 148 | } 149 | 150 | if (!start.isNull()) 151 | lib.libvlc_module_description_list_release(start); 152 | 153 | return ret; 154 | }, 155 | }); 156 | }; 157 | 158 | module.exports = VLC; 159 | --------------------------------------------------------------------------------