├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── lint-v2.yml │ └── lint-v3.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── STATUS.md ├── v2 ├── av.go ├── doc.go ├── equalizer.go ├── errors.go ├── event_manager.go ├── event_registry.go ├── events.go ├── go.mod ├── list_player.go ├── logo.go ├── marquee.go ├── media.go ├── media_list.go ├── media_track.go ├── object_registry.go ├── player.go ├── utils.go ├── version.go └── vlc.go └── v3 ├── av.go ├── doc.go ├── equalizer.go ├── errors.go ├── event_manager.go ├── event_registry.go ├── events.go ├── go.mod ├── list_player.go ├── logo.go ├── marquee.go ├── media.go ├── media_discoverer.go ├── media_list.go ├── media_track.go ├── object_registry.go ├── player.go ├── renderer.go ├── renderer_discoverer.go ├── utils.go ├── version.go └── vlc.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: adrg 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/v3" 5 | schedule: 6 | interval: "daily" 7 | reviewers: 8 | - "adrg" 9 | - package-ecosystem: "gomod" 10 | directory: "/v2" 11 | schedule: 12 | interval: "daily" 13 | reviewers: 14 | - "adrg" 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | reviewers: 20 | - "adrg" 21 | -------------------------------------------------------------------------------- /.github/workflows/lint-v2.yml: -------------------------------------------------------------------------------- 1 | name: Lint v2 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | lint: 13 | strategy: 14 | matrix: 15 | go: [stable] 16 | os: [ubuntu-latest] 17 | runs-on: ${{ matrix.os }} 18 | steps: 19 | - name: Setup 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version: ${{ matrix.go }} 23 | cache: true 24 | 25 | - name: Prerequisites 26 | run: | 27 | sudo apt-get update 28 | sudo apt-get -y install libvlc-dev vlc-plugin-base vlc-plugin-video-output vlc-plugin-access-extra 29 | 30 | - name: Prepare checkout 31 | run: git config --global core.autocrlf false 32 | 33 | - name: Checkout 34 | uses: actions/checkout@v4 35 | 36 | - name: Lint 37 | uses: golangci/golangci-lint-action@v6.5.2 38 | with: 39 | version: latest 40 | args: --timeout=5m 41 | working-directory: ./v2 42 | -------------------------------------------------------------------------------- /.github/workflows/lint-v3.yml: -------------------------------------------------------------------------------- 1 | name: Lint v3 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | lint: 13 | strategy: 14 | matrix: 15 | go: [stable] 16 | os: [ubuntu-latest] 17 | runs-on: ${{ matrix.os }} 18 | steps: 19 | - name: Setup 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version: ${{ matrix.go }} 23 | cache: true 24 | 25 | - name: Prerequisites 26 | run: | 27 | sudo apt-get update 28 | sudo apt-get -y install libvlc-dev vlc-plugin-base vlc-plugin-video-output vlc-plugin-access-extra 29 | 30 | - name: Prepare checkout 31 | run: git config --global core.autocrlf false 32 | 33 | - name: Checkout 34 | uses: actions/checkout@v4 35 | 36 | - name: Lint 37 | uses: golangci/golangci-lint-action@v6.5.2 38 | with: 39 | version: latest 40 | args: --timeout=5m 41 | working-directory: ./v2 42 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, 8 | body size, disability, ethnicity, sex characteristics, gender identity and 9 | expression, level of experience, education, socio-economic status, nationality, 10 | personal appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behaviour that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behaviour by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behaviour and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behaviour. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviour that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behaviour may be 58 | reported by contacting the project team at adrg@epistack.com. All complaints 59 | will be reviewed and investigated and will result in a response that is deemed 60 | necessary and appropriate to the circumstances. The project team is obligated to 61 | maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 71 | version 1.4, available at 72 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 73 | 74 | [homepage]: https://www.contributor-covenant.org 75 | 76 | For answers to common questions about this code of conduct, see 77 | https://www.contributor-covenant.org/faq 78 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to this project 2 | 3 | Contributions in the form of pull requests, issues or just general feedback, 4 | are always welcome. Please take a moment to review this document in order to 5 | make the contribution process easy and effective for everyone involved. 6 | 7 | Following these guidelines helps to communicate that you respect the time of 8 | the developers managing and developing this open source project. In return, 9 | they should reciprocate that respect in addressing your issue or assessing 10 | patches and features. 11 | 12 | ## Using the issue tracker 13 | 14 | The issue tracker is the preferred channel for [bug reports](#bugs), 15 | [features requests](#features) and [submitting pull 16 | requests](#pull-requests), but please respect the following restrictions: 17 | 18 | * Please **do not** use the issue tracker for personal support requests (use 19 | [Stack Overflow](http://stackoverflow.com) or IRC). 20 | * Please **do not** derail or troll issues. Keep the discussion on topic and 21 | respect the opinions of others. 22 | 23 | 24 | ## Bug reports 25 | 26 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 27 | Good bug reports are extremely helpful - thank you! 28 | 29 | Guidelines for bug reports: 30 | 31 | 1. **Use the GitHub issue search** — check if the issue has already been 32 | reported. 33 | 2. **Check if the issue has been fixed** — try to reproduce it using the 34 | latest `master` or development branch in the repository. 35 | 3. **Isolate the problem** — create a reduced test case. 36 | 37 | A good bug report shouldn't leave others needing to chase you up for more 38 | information. Please try to be as detailed as possible in your report. What is 39 | your environment? What steps will reproduce the issue? What browser(s) and OS 40 | experience the problem? What would you expect to be the outcome? All these 41 | details will help people to fix any potential bugs. 42 | 43 | Example: 44 | 45 | > Short and descriptive example bug report title 46 | > 47 | > A summary of the issue and the browser/OS environment in which it occurs. If 48 | > suitable, include the steps required to reproduce the bug. 49 | > 50 | > 1. This is the first step 51 | > 2. This is the second step 52 | > 3. Further steps, etc. 53 | > 54 | > `` - a link to the reduced test case 55 | > 56 | > Any other information you want to share that is relevant to the issue being 57 | > reported. This might include the lines of code that you have identified as 58 | > causing the bug, and potential solutions (and your opinions on their 59 | > merits). 60 | 61 | 62 | 63 | ## Feature requests 64 | 65 | Feature requests are welcome. But take a moment to find out whether your idea 66 | fits with the scope and aims of the project. It's up to *you* to make a strong 67 | case to convince the project's developers of the merits of this feature. Please 68 | provide as much detail and context as possible. 69 | 70 | 71 | 72 | ## Pull requests 73 | 74 | Good pull requests - patches, improvements, new features - are a fantastic 75 | help. They should remain focused in scope and avoid containing unrelated 76 | commits. 77 | 78 | **Please ask first** before embarking on any significant pull request (e.g. 79 | implementing features, refactoring code, porting to a different language), 80 | otherwise you risk spending a lot of time working on something that the 81 | project's developers might not want to merge into the project. 82 | 83 | Please adhere to the coding conventions used throughout a project (indentation, 84 | accurate comments, etc.) and any other requirements (such as test coverage). 85 | 86 | Follow this process if you'd like your work considered for inclusion in the 87 | project: 88 | 89 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 90 | and configure the remotes: 91 | 92 | ```bash 93 | # Clone your fork of the repo into the current directory 94 | git clone https://github.com// 95 | # Navigate to the newly cloned directory 96 | cd 97 | # Assign the original repo to a remote called "upstream" 98 | git remote add upstream https://github.com// 99 | ``` 100 | 101 | 2. If you cloned a while ago, get the latest changes from upstream: 102 | 103 | ```bash 104 | git checkout 105 | git pull upstream 106 | ``` 107 | 108 | 3. Create a new topic branch (off the main project development branch) to 109 | contain your feature, change, or fix: 110 | 111 | ```bash 112 | git checkout -b 113 | ``` 114 | 115 | 4. Commit your changes in logical chunks and use descriptive commit messages. 116 | Use [interactive rebase](https://help.github.com/articles/interactive-rebase) 117 | to tidy up your commits before making them public. 118 | 119 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 120 | 121 | ```bash 122 | git pull [--rebase] upstream 123 | ``` 124 | 125 | 6. Push your topic branch up to your fork: 126 | 127 | ```bash 128 | git push origin 129 | ``` 130 | 131 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 132 | with a clear title and description. 133 | 134 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to 135 | license your work under the same license as that used by the project. 136 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Adrian-George Bostan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | libvlc-go logo 4 |
5 |

6 | 7 |

Handcrafted Go bindings for libVLC and high-level media player interface

8 | 9 |

10 | 11 | Build status 12 | 13 | 14 | pkg.go.dev documentation 15 | 16 | 17 | MIT license 18 | 19 | 20 | Awesome Go 21 | 22 | 23 | Buy me a coffee 24 | 25 |
26 | 27 | Go report card 28 | 29 | 30 | GitHub contributors 31 | 32 | 33 | Discord channel 34 | 35 | 36 | GitHub open issues 37 | 38 | 39 | GitHub closed issues 40 | 41 |

42 | 43 | The package can be useful for adding multimedia capabilities to applications 44 | through the provided player interfaces. It relies on Go modules in order to 45 | mirror each supported major version of [libVLC](https://www.videolan.org/vlc/libvlc.html). 46 | 47 | Documentation for v3, which implements bindings for libVLC 3.X, can be found on [pkg.go.dev](https://pkg.go.dev/github.com/adrg/libvlc-go/v3). 48 | Documentation for v2, which implements bindings for libVLC 2.X, can be found on [pkg.go.dev](https://pkg.go.dev/github.com/adrg/libvlc-go/v2). 49 | 50 |

51 | 52 | libvlc-go media player 53 | 54 | 55 | libvlc-go screen recorder 56 | 57 | 58 | libvlc-go equalizer 59 | 60 | 61 | libvlc-go media discovery 62 | 63 |

64 | 65 | Example applications: 66 | 67 | * [GUI media player](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk3_player) 68 | * [GUI screen recorder](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk3_screen_recorder) 69 | * [GUI equalizer](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk3_equalizer) 70 | * [GUI media discovery](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk3_media_discovery) 71 | 72 | ## Prerequisites 73 | 74 | The libVLC development files are required. Instructions for installing the 75 | VLC SDK on multiple operating systems can be found on the wiki pages of this project. 76 | 77 | - [Install on Linux](https://github.com/adrg/libvlc-go/wiki/Install-on-Linux) 78 | - [Install on Windows](https://github.com/adrg/libvlc-go/wiki/Install-on-Windows) 79 | - [Install on macOS](https://github.com/adrg/libvlc-go/wiki/Install-on-macOS) 80 | 81 | ## Installation 82 | 83 | In order to support multiple versions of libVLC, the package contains a Go 84 | module for each major version of the API. Choose an installation option 85 | depending on the version of libVLC you want to use. 86 | 87 | **libVLC v3.X** 88 | 89 | ```bash 90 | go get github.com/adrg/libvlc-go/v3 91 | ``` 92 | 93 | **libVLC v2.X** 94 | 95 | ```bash 96 | go get github.com/adrg/libvlc-go/v2 97 | 98 | # Build for libVLC < v2.2.0 99 | go build -tags legacy 100 | ``` 101 | 102 | All versions above also work for projects which are not using Go modules. 103 | However, consider switching to modules. 104 | 105 | ## Examples 106 | 107 | * [GTK 3 media player](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk3_player) (using [gotk3](https://github.com/gotk3/gotk3)) 108 | * [GTK 3 screen recorder](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk3_screen_recorder) (using [gotk3](https://github.com/gotk3/gotk3)) 109 | * [GTK 3 media discovery](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk3_media_discovery) (using [gotk3](https://github.com/gotk3/gotk3)) 110 | * [GTK 3 equalizer](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk3_equalizer) (using [gotk3](https://github.com/gotk3/gotk3)) 111 | * [GTK 2 media player](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk2_player) (using [go-gtk](https://github.com/mattn/go-gtk)) 112 | * [GTK 2 screen recorder](https://github.com/adrg/libvlc-go-examples/tree/master/v3/gtk2_screen_recorder) (using [go-gtk](https://github.com/mattn/go-gtk)) 113 | * [Basic player usage](https://github.com/adrg/libvlc-go-examples/blob/master/v3/player/player.go) 114 | * [Basic list player usage](https://github.com/adrg/libvlc-go-examples/tree/master/v3/list_player/list_player.go) 115 | * [Handling events](https://github.com/adrg/libvlc-go-examples/tree/master/v3/event_handling/event_handling.go) 116 | * [Retrieve media tracks](https://github.com/adrg/libvlc-go-examples/blob/master/v3/media_tracks/media_tracks.go) 117 | * [Retrieve media information](https://github.com/adrg/libvlc-go-examples/blob/master/v3/media_information/media_information.go) 118 | * [Display screen as player media](https://github.com/adrg/libvlc-go-examples/blob/master/v3/display_screen_media/display_screen_media.go) 119 | * [Stream media to Chromecast](https://github.com/adrg/libvlc-go-examples/blob/master/v3/chromecast_streaming/chromecast_streaming.go) 120 | * [Player equalizer usage](https://github.com/adrg/libvlc-go-examples/blob/master/v3/equalizer/equalizer.go) 121 | 122 | Examples for all supported API versions can be found at https://github.com/adrg/libvlc-go-examples. 123 | 124 | ## Usage 125 | 126 | ```go 127 | package main 128 | 129 | import ( 130 | "log" 131 | 132 | vlc "github.com/adrg/libvlc-go/v3" 133 | ) 134 | 135 | func main() { 136 | // Initialize libVLC. Additional command line arguments can be passed in 137 | // to libVLC by specifying them in the Init function. 138 | if err := vlc.Init("--no-video", "--quiet"); err != nil { 139 | log.Fatal(err) 140 | } 141 | defer vlc.Release() 142 | 143 | // Create a new player. 144 | player, err := vlc.NewPlayer() 145 | if err != nil { 146 | log.Fatal(err) 147 | } 148 | defer func() { 149 | player.Stop() 150 | player.Release() 151 | }() 152 | 153 | // Add a media file from path or from URL. 154 | // Set player media from path: 155 | // media, err := player.LoadMediaFromPath("localpath/test.mp4") 156 | // Set player media from URL: 157 | media, err := player.LoadMediaFromURL("http://stream-uk1.radioparadise.com/mp3-32") 158 | if err != nil { 159 | log.Fatal(err) 160 | } 161 | defer media.Release() 162 | 163 | // Retrieve player event manager. 164 | manager, err := player.EventManager() 165 | if err != nil { 166 | log.Fatal(err) 167 | } 168 | 169 | // Register the media end reached event with the event manager. 170 | quit := make(chan struct{}) 171 | eventCallback := func(event vlc.Event, userData interface{}) { 172 | close(quit) 173 | } 174 | 175 | eventID, err := manager.Attach(vlc.MediaPlayerEndReached, eventCallback, nil) 176 | if err != nil { 177 | log.Fatal(err) 178 | } 179 | defer manager.Detach(eventID) 180 | 181 | // Start playing the media. 182 | err = player.Play() 183 | if err != nil { 184 | log.Fatal(err) 185 | } 186 | 187 | <-quit 188 | } 189 | ``` 190 | 191 | ## In action 192 | 193 | A list of projects using libvlc-go, in alphabetical order. If you want to 194 | showcase your project in this section, please create a pull request with it. 195 | 196 | - [Alio](https://github.com/fenimore/alio) - Command-line music player with Emacs style key bindings. 197 | - [Tripbot](https://github.com/adanalife/tripbot) - An ongoing 24/7 slow-TV art project. 198 | 199 | ## Stargazers over time 200 | 201 | [![Stargazers over time](https://starchart.cc/adrg/libvlc-go.svg)](https://starchart.cc/adrg/libvlc-go) 202 | 203 | ## Contributing 204 | 205 | Contributions in the form of pull requests, issues or just general feedback, 206 | are always welcome. 207 | See [CONTRIBUTING.MD](CONTRIBUTING.md). 208 | 209 | **Contributors**: 210 | [adrg](https://github.com/adrg), 211 | [fenimore](https://github.com/fenimore), 212 | [tarrsalah](https://github.com/tarrsalah), 213 | [danielpellon](https://github.com/danielpellon), 214 | [patknight](https://github.com/patknight), 215 | [sndnvaps](https://github.com/sndnvaps), 216 | [karlpip](https://github.com/karlpip). 217 | 218 | ## Discord server 219 | 220 | libvlc-go is part of the libVLC Discord Community [server](https://discord.gg/3h3K3JF). Feel free to come say hello! 221 | 222 | ## References 223 | 224 | For more information see the 225 | [libVLC](https://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc.html) documentation. 226 | 227 | ## License 228 | 229 | Copyright (c) 2018 Adrian-George Bostan. 230 | 231 | This project is licensed under the [MIT license](https://opensource.org/licenses/MIT). 232 | See [LICENSE](LICENSE) for more details. 233 | -------------------------------------------------------------------------------- /v2/av.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | // #include 6 | import "C" 7 | import ( 8 | "unsafe" 9 | ) 10 | 11 | // StereoMode defines stereo modes which can be used by an audio output. 12 | type StereoMode int 13 | 14 | // Stereo modes. 15 | const ( 16 | StereoModeError StereoMode = iota - 1 17 | StereoModeNotSet 18 | StereoModeNormal 19 | StereoModeReverse 20 | StereoModeLeft 21 | StereoModeRight 22 | StereoModeDolbySurround 23 | ) 24 | 25 | // Position defines locations of entities relative to a container. 26 | type Position int 27 | 28 | // Positions. 29 | const ( 30 | PositionDisable Position = iota - 1 31 | PositionCenter 32 | PositionLeft 33 | PositionRight 34 | PositionTop 35 | PositionTopLeft 36 | PositionTopRight 37 | PositionBottom 38 | PositionBottomLeft 39 | PositionBottomRight 40 | ) 41 | 42 | // DeinterlaceMode defines deinterlacing modes which can be used when 43 | // rendering videos. 44 | // 45 | // For more information see https://wiki.videolan.org/Deinterlacing. 46 | type DeinterlaceMode string 47 | 48 | // Deinterlace modes. 49 | const ( 50 | DeinterlaceModeDisable DeinterlaceMode = "" 51 | DeinterlaceModeDiscard DeinterlaceMode = "discard" 52 | DeinterlaceModeBlend DeinterlaceMode = "blend" 53 | DeinterlaceModeMean DeinterlaceMode = "mean" 54 | DeinterlaceModeBob DeinterlaceMode = "bob" 55 | DeinterlaceModeLinear DeinterlaceMode = "linear" 56 | DeinterlaceModeX DeinterlaceMode = "x" 57 | DeinterlaceModeYadif DeinterlaceMode = "yadif" 58 | DeinterlaceModeYadif2x DeinterlaceMode = "yadif2x" 59 | DeinterlaceModePhosphor DeinterlaceMode = "phosphor" 60 | DeinterlaceModeIVTC DeinterlaceMode = "ivtc" 61 | ) 62 | 63 | // AudioOutput contains information regarding an audio output. 64 | type AudioOutput struct { 65 | Name string 66 | Description string 67 | } 68 | 69 | // AudioOutputList returns the list of available audio outputs. 70 | // In order to change the audio output of a media player instance, 71 | // use the Player.SetAudioOutput method. 72 | func AudioOutputList() ([]*AudioOutput, error) { 73 | if err := inst.assertInit(); err != nil { 74 | return nil, err 75 | } 76 | 77 | cOutputs := C.libvlc_audio_output_list_get(inst.handle) 78 | if cOutputs == nil { 79 | return nil, errOrDefault(getError(), ErrAudioOutputListMissing) 80 | } 81 | 82 | var outputs []*AudioOutput 83 | for n := cOutputs; n != nil; n = n.p_next { 84 | outputs = append(outputs, &AudioOutput{ 85 | Name: C.GoString(n.psz_name), 86 | Description: C.GoString(n.psz_description), 87 | }) 88 | } 89 | 90 | C.libvlc_audio_output_list_release(cOutputs) 91 | return outputs, nil 92 | } 93 | 94 | // AudioOutputDevice contains information regarding an audio output device. 95 | type AudioOutputDevice struct { 96 | Name string 97 | Description string 98 | } 99 | 100 | // ListAudioOutputDevices returns the list of available devices for the 101 | // specified audio output. Use the AudioOutputList method in order to obtain 102 | // the list of available audio outputs. In order to change the audio output 103 | // device of a media player instance, use Player.SetAudioOutputDevice. 104 | // 105 | // NOTE: Not all audio outputs support this. An empty list of devices does 106 | // not imply that the specified audio output does not work. 107 | // Some audio output devices in the list might not work in some circumstances. 108 | // By default, it is recommended to not specify any explicit audio device. 109 | func ListAudioOutputDevices(output string) ([]*AudioOutputDevice, error) { 110 | if err := inst.assertInit(); err != nil { 111 | return nil, err 112 | } 113 | 114 | cOutput := C.CString(output) 115 | defer C.free(unsafe.Pointer(cOutput)) 116 | return parseAudioOutputDeviceList(C.libvlc_audio_output_device_list_get(inst.handle, cOutput)) 117 | } 118 | 119 | func parseAudioOutputDeviceList(cDevices *C.libvlc_audio_output_device_t) ([]*AudioOutputDevice, error) { 120 | if cDevices == nil { 121 | return nil, errOrDefault(getError(), ErrAudioOutputDeviceListMissing) 122 | } 123 | 124 | var devices []*AudioOutputDevice 125 | for n := cDevices; n != nil; n = n.p_next { 126 | devices = append(devices, &AudioOutputDevice{ 127 | Name: C.GoString(n.psz_device), 128 | Description: C.GoString(n.psz_description), 129 | }) 130 | } 131 | 132 | C.libvlc_audio_output_device_list_release(cDevices) 133 | return devices, nil 134 | } 135 | 136 | // ModuleDescription contains information about a libVLC module. 137 | type ModuleDescription struct { 138 | Name string 139 | ShortName string 140 | LongName string 141 | Help string 142 | } 143 | 144 | // ListAudioFilters returns the list of available audio filters. 145 | func ListAudioFilters() ([]*ModuleDescription, error) { 146 | if err := inst.assertInit(); err != nil { 147 | return nil, err 148 | } 149 | 150 | return parseFilterList(C.libvlc_audio_filter_list_get(inst.handle)) 151 | } 152 | 153 | // ListVideoFilters returns the list of available video filters. 154 | func ListVideoFilters() ([]*ModuleDescription, error) { 155 | if err := inst.assertInit(); err != nil { 156 | return nil, err 157 | } 158 | 159 | return parseFilterList(C.libvlc_video_filter_list_get(inst.handle)) 160 | } 161 | 162 | func parseFilterList(cFilters *C.libvlc_module_description_t) ([]*ModuleDescription, error) { 163 | if cFilters == nil { 164 | return nil, errOrDefault(getError(), ErrFilterListMissing) 165 | } 166 | 167 | var filters []*ModuleDescription 168 | for n := cFilters; n != nil; n = n.p_next { 169 | filters = append(filters, &ModuleDescription{ 170 | Name: C.GoString(n.psz_name), 171 | ShortName: C.GoString(n.psz_shortname), 172 | LongName: C.GoString(n.psz_longname), 173 | Help: C.GoString(n.psz_help), 174 | }) 175 | } 176 | 177 | C.libvlc_module_description_list_release(cFilters) 178 | return filters, nil 179 | } 180 | -------------------------------------------------------------------------------- /v2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package vlc provides Golang bindings for libVLC version 2.X. 3 | 4 | # Usage 5 | 6 | Initialization 7 | 8 | // Initialize libVLC. Additional command line arguments can be passed in 9 | // to libVLC by specifying them in the Init function. 10 | if err := vlc.Init("--no-video", "--quiet"); err != nil { 11 | log.Fatal(err) 12 | } 13 | defer vlc.Release() 14 | 15 | Player example 16 | 17 | // Create a new player. 18 | player, err := vlc.NewPlayer() 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer func() { 23 | player.Stop() 24 | player.Release() 25 | }() 26 | 27 | // Add a media file from path or from URL. 28 | // Set player media from path: 29 | // media, err := player.LoadMediaFromPath("localpath/test.mp4") 30 | // Set player media from URL: 31 | media, err := player.LoadMediaFromURL("http://stream-uk1.radioparadise.com/mp3-32") 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | defer media.Release() 36 | 37 | // Retrieve player event manager. 38 | manager, err := player.EventManager() 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | // Register the media end reached event with the event manager. 44 | quit := make(chan struct{}) 45 | eventCallback := func(event vlc.Event, userData interface{}) { 46 | close(quit) 47 | } 48 | 49 | eventID, err := manager.Attach(vlc.MediaPlayerEndReached, eventCallback, nil) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | defer manager.Detach(eventID) 54 | 55 | // Start playing the media. 56 | if err = player.Play(); err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | <-quit 61 | 62 | List player example 63 | 64 | // Create a new list player. 65 | player, err := vlc.NewListPlayer() 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | defer func() { 70 | player.Stop() 71 | player.Release() 72 | }() 73 | 74 | // Create a new media list. 75 | list, err := vlc.NewMediaList() 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | defer list.Release() 80 | 81 | err = list.AddMediaFromPath("localpath/test1.mp3") 82 | if err != nil { 83 | log.Fatal(err) 84 | } 85 | 86 | err = list.AddMediaFromURL("http://stream-uk1.radioparadise.com/mp3-32") 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | 91 | // Set player media list. 92 | if err = player.SetMediaList(list); err != nil { 93 | log.Fatal(err) 94 | } 95 | 96 | // Media files can be added to the list after the list has been added 97 | // to the player. The player will play these files as well. 98 | err = list.AddMediaFromPath("localpath/test2.mp3") 99 | if err != nil { 100 | log.Fatal(err) 101 | } 102 | 103 | // Retrieve player event manager. 104 | manager, err := player.EventManager() 105 | if err != nil { 106 | log.Fatal(err) 107 | } 108 | 109 | // Register the media end reached event with the event manager. 110 | quit := make(chan struct{}) 111 | eventCallback := func(event vlc.Event, userData interface{}) { 112 | close(quit) 113 | } 114 | 115 | eventID, err := manager.Attach(vlc.MediaListPlayerPlayed, eventCallback, nil) 116 | if err != nil { 117 | log.Fatal(err) 118 | } 119 | defer manager.Detach(eventID) 120 | 121 | // Start playing the media list. 122 | if err = player.Play(); err != nil { 123 | log.Fatal(err) 124 | } 125 | 126 | <-quit 127 | 128 | Handling multiple events example 129 | 130 | // Create a new player. 131 | player, err := vlc.NewPlayer() 132 | if err != nil { 133 | log.Fatal(err) 134 | } 135 | defer func() { 136 | player.Stop() 137 | player.Release() 138 | }() 139 | 140 | // Add a media file from path or from URL. 141 | // Set player media from path: 142 | // media, err := player.LoadMediaFromPath("test.mp3") 143 | // Set player media from URL: 144 | media, err := player.LoadMediaFromURL("http://stream-uk1.radioparadise.com/mp3-32") 145 | if err != nil { 146 | log.Fatal(err) 147 | } 148 | defer media.Release() 149 | 150 | // Retrieve player event manager. 151 | manager, err := player.EventManager() 152 | if err != nil { 153 | log.Fatal(err) 154 | } 155 | 156 | // Create event handler. 157 | quit := make(chan struct{}) 158 | eventCallback := func(event vlc.Event, userData interface{}) { 159 | switch event { 160 | case vlc.MediaPlayerEndReached: 161 | log.Println("Player end reached") 162 | close(quit) 163 | case vlc.MediaPlayerTimeChanged: 164 | media, err := player.Media() 165 | if err != nil { 166 | log.Println(err) 167 | break 168 | } 169 | 170 | stats, err := media.Stats() 171 | if err != nil { 172 | log.Println(err) 173 | break 174 | } 175 | 176 | log.Printf("%+v\n", stats) 177 | } 178 | } 179 | 180 | // Register events with the event manager. 181 | events := []vlc.Event{ 182 | vlc.MediaPlayerTimeChanged, 183 | vlc.MediaPlayerEndReached, 184 | } 185 | 186 | var eventIDs []vlc.EventID 187 | for _, event := range events { 188 | eventID, err := manager.Attach(event, eventCallback, nil) 189 | if err != nil { 190 | log.Fatal(err) 191 | } 192 | 193 | eventIDs = append(eventIDs, eventID) 194 | } 195 | 196 | // De-register attached events. 197 | defer func() { 198 | for _, eventID := range eventIDs { 199 | manager.Detach(eventID) 200 | } 201 | }() 202 | 203 | // Start playing the media. 204 | if err = player.Play(); err != nil { 205 | log.Fatal(err) 206 | } 207 | 208 | <-quit 209 | */ 210 | package vlc 211 | -------------------------------------------------------------------------------- /v2/equalizer.go: -------------------------------------------------------------------------------- 1 | //go:build !legacy 2 | // +build !legacy 3 | 4 | package vlc 5 | 6 | // #cgo LDFLAGS: -lvlc 7 | // #include 8 | import "C" 9 | 10 | // EqualizerPresetCount returns the number of available equalizer presets. 11 | func EqualizerPresetCount() uint { 12 | return uint(C.libvlc_audio_equalizer_get_preset_count()) 13 | } 14 | 15 | // EqualizerPresetName returns the name of the equalizer preset with the 16 | // specified index. The index must be a number greater than or equal to 0 17 | // and less than EqualizerPresetCount(). The function returns an empty string 18 | // for invalid indices. 19 | func EqualizerPresetName(index uint) string { 20 | return C.GoString(C.libvlc_audio_equalizer_get_preset_name(C.uint(index))) 21 | } 22 | 23 | // EqualizerPresetNames returns the names of all available equalizer presets, 24 | // sorted by their indices in ascending order. 25 | func EqualizerPresetNames() []string { 26 | // Get preset count. 27 | count := EqualizerPresetCount() 28 | 29 | // Get preset names. 30 | names := make([]string, 0, count) 31 | for i := uint(0); i < count; i++ { 32 | names = append(names, EqualizerPresetName(i)) 33 | } 34 | 35 | return names 36 | } 37 | 38 | // EqualizerBandCount returns the number of distinct equalizer frequency bands. 39 | func EqualizerBandCount() uint { 40 | return uint(C.libvlc_audio_equalizer_get_band_count()) 41 | } 42 | 43 | // EqualizerBandFrequency returns the frequency of the equalizer band with the 44 | // specified index. The index must be a number greater than or equal to 0 and 45 | // less than EqualizerBandCount(). The function returns -1 for invalid indices. 46 | func EqualizerBandFrequency(index uint) float64 { 47 | return float64(C.libvlc_audio_equalizer_get_band_frequency(C.uint(index))) 48 | } 49 | 50 | // EqualizerBandFrequencies returns the frequencies of all available equalizer 51 | // bands, sorted by their indices in ascending order. 52 | func EqualizerBandFrequencies() []float64 { 53 | // Get band count. 54 | count := EqualizerBandCount() 55 | 56 | // Get band frequencies. 57 | frequencies := make([]float64, 0, count) 58 | for i := uint(0); i < count; i++ { 59 | frequencies = append(frequencies, EqualizerBandFrequency(i)) 60 | } 61 | 62 | return frequencies 63 | } 64 | 65 | // Equalizer represents an audio equalizer. Use Player.SetEqualizer to assign 66 | // the equalizer to a player instance. 67 | type Equalizer struct { 68 | equalizer *C.libvlc_equalizer_t 69 | } 70 | 71 | // NewEqualizer returns a new equalizer with all frequency values set to zero. 72 | func NewEqualizer() (*Equalizer, error) { 73 | equalizer := C.libvlc_audio_equalizer_new() 74 | if equalizer == nil { 75 | return nil, errOrDefault(getError(), ErrEqualizerCreate) 76 | } 77 | 78 | return &Equalizer{equalizer: equalizer}, nil 79 | } 80 | 81 | // NewEqualizerFromPreset returns a new equalizer with the frequency values 82 | // copied from the preset with the specified index. The index must be a number 83 | // greater than or equal to 0 and less than EqualizerPresetCount(). 84 | func NewEqualizerFromPreset(index uint) (*Equalizer, error) { 85 | equalizer := C.libvlc_audio_equalizer_new_from_preset(C.uint(index)) 86 | if equalizer == nil { 87 | return nil, errOrDefault(getError(), ErrEqualizerCreate) 88 | } 89 | 90 | return &Equalizer{equalizer: equalizer}, nil 91 | } 92 | 93 | // Release destroys the equalizer instance. 94 | func (e *Equalizer) Release() error { 95 | if err := e.assertInit(); err != nil { 96 | return nil 97 | } 98 | 99 | C.libvlc_audio_equalizer_release(e.equalizer) 100 | e.equalizer = nil 101 | 102 | return nil 103 | } 104 | 105 | // PreampValue returns the pre-amplification value of the equalizer in Hz. 106 | func (e *Equalizer) PreampValue() (float64, error) { 107 | if err := e.assertInit(); err != nil { 108 | return 0, err 109 | } 110 | 111 | value := C.libvlc_audio_equalizer_get_preamp(e.equalizer) 112 | return float64(value), nil 113 | } 114 | 115 | // SetPreampValue sets the pre-amplification value of the equalizer. 116 | // The specified amplification value is clamped to the [-20.0, 20.0] Hz range. 117 | func (e *Equalizer) SetPreampValue(value float64) error { 118 | if err := e.assertInit(); err != nil { 119 | return err 120 | } 121 | 122 | if C.libvlc_audio_equalizer_set_preamp(e.equalizer, C.float(value)) != 0 { 123 | return errOrDefault(getError(), ErrEqualizerAmpValueSet) 124 | } 125 | 126 | return nil 127 | } 128 | 129 | // AmpValueAtIndex returns the amplification value for the equalizer frequency 130 | // band with the specified index, in Hz. The index must be a number greater 131 | // than or equal to 0 and less than EqualizerBandCount(). 132 | func (e *Equalizer) AmpValueAtIndex(index uint) (float64, error) { 133 | if err := e.assertInit(); err != nil { 134 | return 0, err 135 | } 136 | 137 | value := C.libvlc_audio_equalizer_get_amp_at_index(e.equalizer, C.uint(index)) 138 | return float64(value), nil 139 | } 140 | 141 | // SetAmpValueAtIndex sets the amplification value for the equalizer frequency 142 | // band with the specified index, in Hz. The index must be a number greater 143 | // than or equal to 0 and less than EqualizerBandCount(). 144 | func (e *Equalizer) SetAmpValueAtIndex(value float64, index uint) error { 145 | if err := e.assertInit(); err != nil { 146 | return err 147 | } 148 | 149 | if C.libvlc_audio_equalizer_set_amp_at_index(e.equalizer, C.float(value), C.uint(index)) != 0 { 150 | return errOrDefault(getError(), ErrEqualizerAmpValueSet) 151 | } 152 | 153 | return nil 154 | } 155 | 156 | func (e *Equalizer) assertInit() error { 157 | if e == nil || e.equalizer == nil { 158 | return ErrEqualizerNotInitialized 159 | } 160 | 161 | return nil 162 | } 163 | -------------------------------------------------------------------------------- /v2/errors.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | import "errors" 4 | 5 | // Generic errors. 6 | var ( 7 | ErrInvalid = errors.New("the provided value is not valid") 8 | ) 9 | 10 | // Module errors. 11 | var ( 12 | ErrModuleInitialize = errors.New("could not initialize module") 13 | ErrModuleNotInitialized = errors.New("module not initialized") 14 | ErrUserInterfaceStart = errors.New("could not start user interface") 15 | ) 16 | 17 | // Player errors. 18 | var ( 19 | ErrPlayerCreate = errors.New("could not create player") 20 | ErrPlayerNotInitialized = errors.New("player is not initialized") 21 | ErrPlayerPlay = errors.New("cannot play the requested media") 22 | ErrPlayerSetVolume = errors.New("could not set player volume") 23 | ErrPlayerSetEqualizer = errors.New("could not set player equalizer") 24 | ) 25 | 26 | // List player errors. 27 | var ( 28 | ErrListPlayerCreate = errors.New("could not create list player") 29 | ErrListPlayerNotInitialized = errors.New("list player is not initialized") 30 | ) 31 | 32 | // Media errors. 33 | var ( 34 | ErrMediaCreate = errors.New("could not create media") 35 | ErrMediaNotFound = errors.New("could not find media") 36 | ErrMediaNotInitialized = errors.New("media is not initialized") 37 | ErrMediaListCreate = errors.New("could not create media list") 38 | ErrMediaListNotFound = errors.New("could not find media list") 39 | ErrMediaListNotInitialized = errors.New("media list is not initialized") 40 | ErrMediaListReadOnly = errors.New("media list is read-only") 41 | ErrMediaListActionFailed = errors.New("could not perform media list action") 42 | ErrMissingMediaStats = errors.New("could not get media statistics") 43 | ErrInvalidMediaStats = errors.New("invalid media statistics") 44 | ErrMissingMediaLocation = errors.New("could not get media location") 45 | ErrMissingMediaDimensions = errors.New("could not get media dimensions") 46 | ErrMediaMetaSave = errors.New("could not save media metadata") 47 | ErrMediaNotParsed = errors.New("media is not parsed") 48 | ) 49 | 50 | // Media track errors. 51 | var ( 52 | ErrMediaTrackNotInitialized = errors.New("media track is not initialized") 53 | ErrMediaTrackNotFound = errors.New("could not find media track") 54 | ErrInvalidMediaTrack = errors.New("invalid media track") 55 | ) 56 | 57 | // Event manager errors. 58 | var ( 59 | ErrMissingEventManager = errors.New("could not get event manager instance") 60 | ErrInvalidEventCallback = errors.New("invalid event callback") 61 | ) 62 | 63 | // Audio/Video errors. 64 | var ( 65 | ErrAudioOutputListMissing = errors.New("could not get audio output list") 66 | ErrAudioOutputSet = errors.New("could not set audio output") 67 | ErrAudioOutputDeviceListMissing = errors.New("could not get audio output device list") 68 | ErrFilterListMissing = errors.New("could not get filter list") 69 | ErrStereoModeSet = errors.New("could not set stereo mode") 70 | ErrVideoSnapshot = errors.New("could not take video snapshot") 71 | ErrCursorPositionMissing = errors.New("could not get cursor position") 72 | ) 73 | 74 | // Equalizer errors. 75 | var ( 76 | ErrEqualizerCreate = errors.New("could not create equalizer") 77 | ErrEqualizerNotInitialized = errors.New("equalizer is not initialized") 78 | ErrEqualizerAmpValueSet = errors.New("could not set equalizer amplification value") 79 | ErrEventAttach = errors.New("could not attach event") 80 | ) 81 | -------------------------------------------------------------------------------- /v2/event_manager.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | /* 4 | #cgo LDFLAGS: -lvlc 5 | #include 6 | 7 | typedef const libvlc_event_t constev; 8 | extern void eventDispatch(constev*, void*); 9 | 10 | static inline int eventAttach(libvlc_event_manager_t* em, libvlc_event_type_t et, unsigned long userData) { 11 | return libvlc_event_attach(em, et, eventDispatch, (void*)userData); 12 | } 13 | static inline int eventDetach(libvlc_event_manager_t* em, libvlc_event_type_t et, unsigned long userData) { 14 | libvlc_event_detach(em, et, eventDispatch, (void*)userData); 15 | } 16 | */ 17 | import "C" 18 | 19 | import ( 20 | "unsafe" 21 | ) 22 | 23 | // EventManager wraps a libvlc event manager. 24 | type EventManager struct { 25 | manager *C.libvlc_event_manager_t 26 | } 27 | 28 | // newEventManager returns a new event manager instance. 29 | func newEventManager(manager *C.libvlc_event_manager_t) *EventManager { 30 | return &EventManager{ 31 | manager: manager, 32 | } 33 | } 34 | 35 | // Attach registers a callback for an event notification. 36 | func (em *EventManager) Attach(event Event, callback EventCallback, userData interface{}) (EventID, error) { 37 | return em.attach(event, callback, nil, userData) 38 | } 39 | 40 | // attach registers callbacks for an event notification. 41 | func (em *EventManager) attach(event Event, externalCallback EventCallback, 42 | internalCallback internalEventCallback, userData interface{}) (EventID, error) { 43 | if err := inst.assertInit(); err != nil { 44 | return 0, err 45 | } 46 | if externalCallback == nil && internalCallback == nil { 47 | return 0, ErrInvalidEventCallback 48 | } 49 | 50 | id := inst.events.add(event, externalCallback, internalCallback, userData) 51 | if C.eventAttach(em.manager, C.libvlc_event_type_t(event), C.ulong(id)) != 0 { 52 | return 0, errOrDefault(getError(), ErrEventAttach) 53 | } 54 | 55 | return id, nil 56 | } 57 | 58 | // Detach unregisters the specified event notification. 59 | func (em *EventManager) Detach(eventIDs ...EventID) { 60 | if err := inst.assertInit(); err != nil { 61 | return 62 | } 63 | 64 | for _, eventID := range eventIDs { 65 | ctx, ok := inst.events.get(eventID) 66 | if !ok { 67 | continue 68 | } 69 | 70 | inst.events.remove(eventID) 71 | C.eventDetach(em.manager, C.libvlc_event_type_t(ctx.event), C.ulong(eventID)) 72 | } 73 | } 74 | 75 | //export eventDispatch 76 | func eventDispatch(event *C.constev, userData unsafe.Pointer) { 77 | if err := inst.assertInit(); err != nil { 78 | return 79 | } 80 | 81 | ctx, ok := inst.events.get(EventID(uintptr(userData))) 82 | if !ok { 83 | return 84 | } 85 | 86 | // Execute external callback. 87 | if ctx.externalCallback != nil { 88 | ctx.externalCallback(ctx.event, ctx.userData) 89 | } 90 | 91 | // Execute internal callback. 92 | if ctx.internalCallback != nil { 93 | ctx.internalCallback(event, ctx.userData) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /v2/event_registry.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | import "C" 6 | import "sync" 7 | 8 | // EventID uniquely identifies a registered event. 9 | type EventID uint64 10 | 11 | // EventCallback represents an event notification callback function. 12 | type EventCallback func(Event, interface{}) 13 | 14 | type internalEventCallback func(*C.libvlc_event_t, interface{}) 15 | 16 | type eventContext struct { 17 | event Event 18 | externalCallback EventCallback 19 | internalCallback internalEventCallback 20 | userData interface{} 21 | } 22 | 23 | type eventRegistry struct { 24 | sync.RWMutex 25 | 26 | contexts map[EventID]*eventContext 27 | sequence EventID 28 | } 29 | 30 | func newEventRegistry() *eventRegistry { 31 | return &eventRegistry{ 32 | contexts: map[EventID]*eventContext{}, 33 | } 34 | } 35 | 36 | func (er *eventRegistry) get(id EventID) (*eventContext, bool) { 37 | if id == 0 { 38 | return nil, false 39 | } 40 | 41 | er.RLock() 42 | ctx, ok := er.contexts[id] 43 | er.RUnlock() 44 | 45 | return ctx, ok 46 | } 47 | 48 | func (er *eventRegistry) add(event Event, externalCallback EventCallback, 49 | internalCallback internalEventCallback, userData interface{}) EventID { 50 | er.Lock() 51 | 52 | er.sequence++ 53 | id := er.sequence 54 | 55 | er.contexts[id] = &eventContext{ 56 | event: event, 57 | externalCallback: externalCallback, 58 | internalCallback: internalCallback, 59 | userData: userData, 60 | } 61 | 62 | er.Unlock() 63 | return id 64 | } 65 | 66 | func (er *eventRegistry) remove(id EventID) { 67 | if id == 0 { 68 | return 69 | } 70 | 71 | er.Lock() 72 | delete(er.contexts, id) 73 | er.Unlock() 74 | } 75 | -------------------------------------------------------------------------------- /v2/events.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // Event represents an event that can occur inside libvlc. 4 | type Event int 5 | 6 | // Media events. 7 | const ( 8 | // MediaMetaChanged is triggered when the metadata of a media item changes. 9 | MediaMetaChanged Event = iota 10 | 11 | // MediaSubItemAdded is triggered when a Subitem is added to a media item. 12 | MediaSubItemAdded 13 | 14 | // MediaDurationChanged is triggered when the duration 15 | // of a media item changes. 16 | MediaDurationChanged 17 | 18 | // MediaParsedChanged is triggered when the parsing state 19 | // of a media item changes. 20 | MediaParsedChanged 21 | 22 | // MediaFreed is triggered when a media item is freed. 23 | MediaFreed 24 | 25 | // MediaStateChanged is triggered when the state of the media item changes. 26 | MediaStateChanged 27 | 28 | // MediaSubItemTreeAdded is triggered when a Subitem tree is 29 | // added to a media item. 30 | MediaSubItemTreeAdded 31 | 32 | // MediaThumbnailGenerated is triggered when a thumbnail 33 | // generation is completed. 34 | MediaThumbnailGenerated 35 | ) 36 | 37 | // Player events. 38 | const ( 39 | MediaPlayerMediaChanged Event = 0x100 + iota 40 | MediaPlayerNothingSpecial 41 | MediaPlayerOpening 42 | MediaPlayerBuffering 43 | MediaPlayerPlaying 44 | MediaPlayerPaused 45 | MediaPlayerStopped 46 | MediaPlayerForward 47 | MediaPlayerBackward 48 | MediaPlayerEndReached 49 | MediaPlayerEncounteredError 50 | MediaPlayerTimeChanged 51 | MediaPlayerPositionChanged 52 | MediaPlayerSeekableChanged 53 | MediaPlayerPausableChanged 54 | MediaPlayerTitleChanged 55 | MediaPlayerSnapshotTaken 56 | MediaPlayerLengthChanged 57 | MediaPlayerVout 58 | MediaPlayerScrambledChanged 59 | MediaPlayerESAdded 60 | MediaPlayerESDeleted 61 | MediaPlayerESSelected 62 | MediaPlayerCorked 63 | MediaPlayerUncorked 64 | MediaPlayerMuted 65 | MediaPlayerUnmuted 66 | MediaPlayerAudioVolume 67 | MediaPlayerAudioDevice 68 | MediaPlayerChapterChanged 69 | ) 70 | 71 | // Media list events. 72 | const ( 73 | // MediaListItemAdded is triggered when a media item is added to a media list. 74 | MediaListItemAdded Event = 0x200 + iota 75 | 76 | // MediaListWillAddItem is triggered when a media item is about to get 77 | // added to a media list. 78 | MediaListWillAddItem 79 | 80 | // MediaListItemDeleted is triggered when a media item is deleted 81 | // from a media list. 82 | MediaListItemDeleted 83 | 84 | // MediaListWillDeleteItem is triggered when a media item is about to get 85 | // deleted from a media list. 86 | MediaListWillDeleteItem 87 | 88 | // MediaListEndReached is triggered when a media list has reached the end. 89 | MediaListEndReached 90 | ) 91 | 92 | // Deprecated events. 93 | const ( 94 | MediaListViewItemAdded = 0x300 + iota 95 | MediaListViewWillAddItem 96 | MediaListViewItemDeleted 97 | MediaListViewWillDeleteItem 98 | ) 99 | 100 | const ( 101 | // MediaListPlayerPlayed is triggered when playback of the media list 102 | // of the list player has ended. 103 | MediaListPlayerPlayed = 0x400 + iota 104 | 105 | // MediaListPlayerNextItemSet is triggered when the current item 106 | // of a media list player has changed to a different item. 107 | MediaListPlayerNextItemSet 108 | 109 | // MediaListPlayerStopped is triggered when playback 110 | // of a media list player is stopped programmatically. 111 | MediaListPlayerStopped 112 | ) 113 | 114 | // Deprecated events. 115 | const ( 116 | MediaDiscovererStarted Event = 0x500 + iota 117 | MediaDiscovererEnded 118 | ) 119 | 120 | // Renderer events. 121 | const ( 122 | // RendererDiscovererItemAdded is triggered when a new renderer item is 123 | // found by a renderer discoverer. The renderer item is valid until deleted. 124 | RendererDiscovererItemAdded Event = 0x502 + iota 125 | 126 | // RendererDiscovererItemDeleted is triggered when a previously discovered 127 | // renderer item was deleted by a renderer discoverer. The renderer item 128 | // is no longer valid. 129 | RendererDiscovererItemDeleted 130 | ) 131 | 132 | // VideoLAN Manager events. 133 | const ( 134 | VlmMediaAdded Event = 0x600 + iota 135 | VlmMediaRemoved 136 | VlmMediaChanged 137 | VlmMediaInstanceStarted 138 | VlmMediaInstanceStopped 139 | VlmMediaInstanceStatusInit 140 | VlmMediaInstanceStatusOpening 141 | VlmMediaInstanceStatusPlaying 142 | VlmMediaInstanceStatusPause 143 | VlmMediaInstanceStatusEnd 144 | VlmMediaInstanceStatusError 145 | ) 146 | -------------------------------------------------------------------------------- /v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/adrg/libvlc-go/v2 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /v2/list_player.go: -------------------------------------------------------------------------------- 1 | //go:build !legacy 2 | // +build !legacy 3 | 4 | package vlc 5 | 6 | // #cgo LDFLAGS: -lvlc 7 | // #include 8 | // #include 9 | import "C" 10 | 11 | // PlaybackMode defines playback modes for a media list. 12 | type PlaybackMode uint 13 | 14 | // Playback modes. 15 | const ( 16 | Default PlaybackMode = iota 17 | Loop 18 | Repeat 19 | ) 20 | 21 | // Validate checks if the playback mode valid. 22 | func (pm PlaybackMode) Validate() error { 23 | if pm > Repeat { 24 | return ErrInvalid 25 | } 26 | 27 | return nil 28 | } 29 | 30 | // ListPlayer is an enhanced media player used to play media lists. 31 | type ListPlayer struct { 32 | player *C.libvlc_media_list_player_t 33 | list *MediaList 34 | } 35 | 36 | // NewListPlayer creates a new list player instance. 37 | func NewListPlayer() (*ListPlayer, error) { 38 | if err := inst.assertInit(); err != nil { 39 | return nil, err 40 | } 41 | 42 | player := C.libvlc_media_list_player_new(inst.handle) 43 | if player == nil { 44 | return nil, errOrDefault(getError(), ErrListPlayerCreate) 45 | } 46 | 47 | return &ListPlayer{player: player}, nil 48 | } 49 | 50 | // Release destroys the list player instance. 51 | func (lp *ListPlayer) Release() error { 52 | if err := lp.assertInit(); err != nil { 53 | return nil 54 | } 55 | 56 | C.libvlc_media_list_player_release(lp.player) 57 | lp.player = nil 58 | 59 | return nil 60 | } 61 | 62 | // Player returns the underlying Player instance of the list player. 63 | func (lp *ListPlayer) Player() (*Player, error) { 64 | if err := lp.assertInit(); err != nil { 65 | return nil, err 66 | } 67 | 68 | player := C.libvlc_media_list_player_get_media_player(lp.player) 69 | if player == nil { 70 | return nil, errOrDefault(getError(), ErrPlayerNotInitialized) 71 | } 72 | 73 | // This call will not release the player. Instead, it will decrement the 74 | // reference count increased by libvlc_media_list_player_get_media_player. 75 | C.libvlc_media_player_release(player) 76 | 77 | return &Player{player: player}, nil 78 | } 79 | 80 | // SetPlayer sets the underlying Player instance of the list player. 81 | func (lp *ListPlayer) SetPlayer(player *Player) error { 82 | if err := lp.assertInit(); err != nil { 83 | return err 84 | } 85 | if err := player.assertInit(); err != nil { 86 | return err 87 | } 88 | 89 | C.libvlc_media_list_player_set_media_player(lp.player, player.player) 90 | return nil 91 | } 92 | 93 | // Play plays the current media list. 94 | func (lp *ListPlayer) Play() error { 95 | if err := lp.assertInit(); err != nil { 96 | return err 97 | } 98 | if lp.IsPlaying() { 99 | return nil 100 | } 101 | 102 | C.libvlc_media_list_player_play(lp.player) 103 | return getError() 104 | } 105 | 106 | // PlayNext plays the next media in the current media list. 107 | func (lp *ListPlayer) PlayNext() error { 108 | if err := lp.assertInit(); err != nil { 109 | return err 110 | } 111 | 112 | if C.libvlc_media_list_player_next(lp.player) < 0 { 113 | return errOrDefault(getError(), ErrPlayerPlay) 114 | } 115 | 116 | return nil 117 | } 118 | 119 | // PlayPrevious plays the previous media in the current media list. 120 | func (lp *ListPlayer) PlayPrevious() error { 121 | if err := lp.assertInit(); err != nil { 122 | return err 123 | } 124 | 125 | if C.libvlc_media_list_player_previous(lp.player) < 0 { 126 | return errOrDefault(getError(), ErrPlayerPlay) 127 | } 128 | 129 | return nil 130 | } 131 | 132 | // PlayAtIndex plays the media at the specified index from the 133 | // current media list. 134 | func (lp *ListPlayer) PlayAtIndex(index uint) error { 135 | if err := lp.assertInit(); err != nil { 136 | return err 137 | } 138 | 139 | idx := C.int(index) 140 | if C.libvlc_media_list_player_play_item_at_index(lp.player, idx) < 0 { 141 | return errOrDefault(getError(), ErrPlayerPlay) 142 | } 143 | 144 | return nil 145 | } 146 | 147 | // PlayItem plays the specified media item. The item must be part of the 148 | // current media list of the player. 149 | func (lp *ListPlayer) PlayItem(m *Media) error { 150 | if err := lp.assertInit(); err != nil { 151 | return err 152 | } 153 | if err := m.assertInit(); err != nil { 154 | return err 155 | } 156 | 157 | if C.libvlc_media_list_player_play_item(lp.player, m.media) < 0 { 158 | return errOrDefault(getError(), ErrMediaNotFound) 159 | } 160 | 161 | return nil 162 | } 163 | 164 | // IsPlaying returns a boolean value specifying if the player is currently 165 | // playing. 166 | func (lp *ListPlayer) IsPlaying() bool { 167 | if err := lp.assertInit(); err != nil { 168 | return false 169 | } 170 | 171 | return C.libvlc_media_list_player_is_playing(lp.player) != 0 172 | } 173 | 174 | // Stop cancels the currently playing media list, if there is one. 175 | func (lp *ListPlayer) Stop() error { 176 | if err := lp.assertInit(); err != nil { 177 | return err 178 | } 179 | 180 | C.libvlc_media_list_player_stop(lp.player) 181 | return getError() 182 | } 183 | 184 | // TogglePause pauses/resumes the player. 185 | // Calling this method has no effect if there is no media. 186 | func (lp *ListPlayer) TogglePause() error { 187 | if err := lp.assertInit(); err != nil { 188 | return err 189 | } 190 | 191 | C.libvlc_media_list_player_pause(lp.player) 192 | return getError() 193 | } 194 | 195 | // SetPlaybackMode sets the player playback mode for the media list. 196 | // By default, it plays the media list once and then stops. 197 | func (lp *ListPlayer) SetPlaybackMode(mode PlaybackMode) error { 198 | if err := lp.assertInit(); err != nil { 199 | return err 200 | } 201 | if err := mode.Validate(); err != nil { 202 | return err 203 | } 204 | 205 | C.libvlc_media_list_player_set_playback_mode( 206 | lp.player, 207 | C.libvlc_playback_mode_t(mode), 208 | ) 209 | return nil 210 | } 211 | 212 | // MediaState returns the state of the current media. 213 | func (lp *ListPlayer) MediaState() (MediaState, error) { 214 | if err := lp.assertInit(); err != nil { 215 | return MediaNothingSpecial, err 216 | } 217 | 218 | return MediaState(C.libvlc_media_list_player_get_state(lp.player)), nil 219 | } 220 | 221 | // MediaList returns the current media list of the player, if one exists. 222 | func (lp *ListPlayer) MediaList() *MediaList { 223 | return lp.list 224 | } 225 | 226 | // SetMediaList sets the media list to be played. 227 | func (lp *ListPlayer) SetMediaList(ml *MediaList) error { 228 | if err := lp.assertInit(); err != nil { 229 | return err 230 | } 231 | if err := ml.assertInit(); err != nil { 232 | return err 233 | } 234 | 235 | lp.list = ml 236 | C.libvlc_media_list_player_set_media_list(lp.player, ml.list) 237 | 238 | return nil 239 | } 240 | 241 | // EventManager returns the event manager responsible for the list player. 242 | func (lp *ListPlayer) EventManager() (*EventManager, error) { 243 | if err := lp.assertInit(); err != nil { 244 | return nil, err 245 | } 246 | 247 | manager := C.libvlc_media_list_player_event_manager(lp.player) 248 | if manager == nil { 249 | return nil, ErrMissingEventManager 250 | } 251 | 252 | return newEventManager(manager), nil 253 | } 254 | 255 | func (lp *ListPlayer) assertInit() error { 256 | if lp == nil || lp.player == nil { 257 | return ErrListPlayerNotInitialized 258 | } 259 | 260 | return nil 261 | } 262 | -------------------------------------------------------------------------------- /v2/logo.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | /* 4 | #cgo LDFLAGS: -lvlc 5 | #include 6 | #include 7 | */ 8 | import "C" 9 | import ( 10 | "fmt" 11 | "image" 12 | "image/png" 13 | "os" 14 | "strings" 15 | "time" 16 | "unsafe" 17 | ) 18 | 19 | // LogoFile represents a logo file which can be used as a media player's logo. 20 | // The logo of a player can also be composed of a series of alternating files. 21 | type LogoFile struct { 22 | path string 23 | displayDuration time.Duration 24 | opacity int 25 | } 26 | 27 | // NewLogoFileFromPath returns a new logo file with the specified path. 28 | // The file is displyed for the provided duration. If the specified display 29 | // duration is negative, the global display duration set on the logo the file 30 | // is applied to is used. 31 | // The provided opacity must be a value between 0 (transparent) and 255 (opaque). 32 | // If the specified opacity is negative, the global opacity set on the logo 33 | // the file is applied to is used. 34 | func NewLogoFileFromPath(path string, displayDuration time.Duration, opacity int) (*LogoFile, error) { 35 | if path == "" { 36 | return nil, ErrInvalid 37 | } 38 | 39 | return &LogoFile{ 40 | path: path, 41 | displayDuration: displayDuration, 42 | opacity: opacity, 43 | }, nil 44 | } 45 | 46 | // NewLogoFileFromImage returns a new logo file with the specified image. 47 | // The file is displyed for the provided duration. If the specified display 48 | // duration is negative, the global display duration set on the logo the file 49 | // is applied to is used. 50 | // The provided opacity must be a value between 0 (transparent) and 255 (opaque). 51 | // If the specified opacity is negative, the global opacity set on the logo 52 | // the file is applied to is used. 53 | func NewLogoFileFromImage(img image.Image, displayDuration time.Duration, opacity int) (*LogoFile, error) { 54 | if img == nil { 55 | return nil, ErrInvalid 56 | } 57 | 58 | // Create temporary file. 59 | f, err := os.CreateTemp("", "logo_*.png") 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | // Encode logo image to temporary file. 65 | if err := png.Encode(f, img); err != nil { 66 | return nil, err 67 | } 68 | 69 | // Close temporary file. 70 | path := f.Name() 71 | if err := f.Close(); err != nil { 72 | return nil, err 73 | } 74 | 75 | return NewLogoFileFromPath(path, displayDuration, opacity) 76 | } 77 | 78 | // Logo represents a logo that can be displayed over a media instance. 79 | // 80 | // For more information see https://wiki.videolan.org/Documentation:Modules/logo. 81 | type Logo struct { 82 | player *Player 83 | } 84 | 85 | func newLogo(player *Player) *Logo { 86 | return &Logo{ 87 | player: player, 88 | } 89 | } 90 | 91 | // Enable enables or disables the logo. By default, the logo is disabled. 92 | func (l *Logo) Enable(enable bool) error { 93 | return l.setInt(C.libvlc_logo_enable, boolToInt(enable)) 94 | } 95 | 96 | // SetFiles sets the sequence of files to be displayed for the logo. 97 | func (l *Logo) SetFiles(files ...*LogoFile) error { 98 | fileFmts := make([]string, 0, len(files)) 99 | for _, file := range files { 100 | if file == nil { 101 | continue 102 | } 103 | fileFmt := file.path 104 | if file.displayDuration >= 0 { 105 | fileFmt = fmt.Sprintf("%s,%d", fileFmt, file.displayDuration.Milliseconds()) 106 | } 107 | if file.opacity >= 0 { 108 | fileFmt = fmt.Sprintf("%s,%d", fileFmt, file.opacity) 109 | } 110 | 111 | fileFmts = append(fileFmts, fileFmt) 112 | } 113 | 114 | if len(fileFmts) == 0 { 115 | return nil 116 | } 117 | 118 | return l.setString(C.libvlc_logo_file, strings.Join(fileFmts, ";")) 119 | } 120 | 121 | // Position returns the position of the logo, relative to its container. 122 | // Default: vlc.PositionTopLeft. 123 | func (l *Logo) Position() (Position, error) { 124 | iVal, err := l.getInt(C.libvlc_logo_position) 125 | if err != nil { 126 | return PositionDisable, err 127 | } 128 | 129 | switch { 130 | case iVal >= 8: 131 | iVal -= 2 132 | case iVal >= 4: 133 | iVal-- 134 | } 135 | 136 | if iVal < 0 { 137 | return PositionTopLeft, nil 138 | } 139 | return Position(iVal), nil 140 | } 141 | 142 | // SetPosition sets the position of the logo, relative to its container. 143 | func (l *Logo) SetPosition(position Position) error { 144 | switch { 145 | case position >= PositionBottom: 146 | position += 2 147 | case position >= PositionTop: 148 | position++ 149 | } 150 | 151 | return l.setInt(C.libvlc_logo_position, int(position)) 152 | } 153 | 154 | // X returns the X coordinate of the logo. The returned value is 155 | // relative to the position of the logo inside its container, i.e. the 156 | // position set using Logo.SetPosition method. 157 | // Default: 0. 158 | func (l *Logo) X() (int, error) { 159 | return l.getInt(C.libvlc_logo_x) 160 | } 161 | 162 | // SetX sets the X coordinate of the logo. The value is specified 163 | // relative to the position of the logo inside its container, i.e. the 164 | // position set using the `Logo.SetPosition` method. 165 | // 166 | // NOTE: the method has no effect if the position of the logo is set to 167 | // `vlc.PositionCenter`, `vlc.PositionTop` or `vlc.PositionBottom`. 168 | func (l *Logo) SetX(x int) error { 169 | return l.setInt(C.libvlc_logo_x, x) 170 | } 171 | 172 | // Y returns the Y coordinate of the logo. The returned value is 173 | // relative to the position of the logo inside its container, i.e. the 174 | // position set using the `Logo.SetPosition` method. 175 | // Default: 0. 176 | func (l *Logo) Y() (int, error) { 177 | return l.getInt(C.libvlc_logo_y) 178 | } 179 | 180 | // SetY sets the Y coordinate of the logo. The value is specified 181 | // relative to the position of the logo inside its container. 182 | // 183 | // NOTE: the method has no effect if the position of the logo is set to 184 | // `vlc.PositionCenter`, `vlc.PositionLeft` or `vlc.PositionRight`. 185 | func (l *Logo) SetY(y int) error { 186 | return l.setInt(C.libvlc_logo_y, y) 187 | } 188 | 189 | // Opacity returns the global opacity of the logo. 190 | // The returned opacity is a value between 0 (transparent) and 255 (opaque). 191 | // The global opacity can be overridden by each provided logo file. 192 | // Default: 255. 193 | func (l *Logo) Opacity() (int, error) { 194 | return l.getInt(C.libvlc_logo_opacity) 195 | } 196 | 197 | // SetOpacity sets the global opacity of the logo. If an opacity override is 198 | // not specified when setting the logo files, the global opacity is used. The 199 | // opacity is specified as an integer between 0 (transparent) and 255 (opaque). 200 | // The global opacity can be overridden by each provided logo file. 201 | func (l *Logo) SetOpacity(opacity int) error { 202 | return l.setInt(C.libvlc_logo_opacity, opacity) 203 | } 204 | 205 | // DisplayDuration returns the global duration for which a logo file 206 | // is set to be displayed before displaying the next one (if one is available). 207 | // The global display duration can be overridden by each provided logo file. 208 | // Default: 1s. 209 | func (l *Logo) DisplayDuration() (time.Duration, error) { 210 | iVal, err := l.getInt(C.libvlc_logo_delay) 211 | return time.Duration(iVal) * time.Millisecond, err 212 | } 213 | 214 | // SetDisplayDuration sets the duration for which to display a logo file 215 | // before displaying the next one (if one is available). 216 | // The global display duration can be overridden by each provided logo file. 217 | func (l *Logo) SetDisplayDuration(displayDuration time.Duration) error { 218 | return l.setInt(C.libvlc_logo_delay, int(displayDuration.Milliseconds())) 219 | } 220 | 221 | // RepeatCount returns the number of times the logo sequence is set 222 | // to be repeated. 223 | func (l *Logo) RepeatCount() (int, error) { 224 | return l.getInt(C.libvlc_logo_repeat) 225 | } 226 | 227 | // SetRepeatCount sets the number of times the logo sequence should repeat. 228 | // Pass in `-1` to repeat the logo sequence indefinitely, `0` to disable logo 229 | // sequence looping or a positive number to repeat the logo sequence a specific 230 | // number of times. 231 | // Default: -1 (the logo sequence is repeated indefinitely). 232 | func (l *Logo) SetRepeatCount(count int) error { 233 | if count > 0 { 234 | count++ 235 | } 236 | 237 | return l.setInt(C.libvlc_logo_repeat, count) 238 | } 239 | 240 | func (l *Logo) getInt(option C.uint) (int, error) { 241 | if err := l.player.assertInit(); err != nil { 242 | return 0, err 243 | } 244 | 245 | return int(C.libvlc_video_get_logo_int(l.player.player, option)), nil 246 | } 247 | 248 | func (l *Logo) setInt(option C.uint, val int) error { 249 | if err := l.player.assertInit(); err != nil { 250 | return err 251 | } 252 | 253 | C.libvlc_video_set_logo_int(l.player.player, option, C.int(val)) 254 | return nil 255 | } 256 | 257 | func (l *Logo) setString(option C.uint, val string) error { 258 | if err := l.player.assertInit(); err != nil { 259 | return err 260 | } 261 | 262 | cVal := C.CString(val) 263 | defer C.free(unsafe.Pointer(cVal)) 264 | 265 | C.libvlc_video_set_logo_string(l.player.player, option, cVal) 266 | return nil 267 | } 268 | -------------------------------------------------------------------------------- /v2/marquee.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | /* 4 | #cgo LDFLAGS: -lvlc 5 | #include 6 | #include 7 | */ 8 | import "C" 9 | import ( 10 | "image/color" 11 | "time" 12 | "unsafe" 13 | ) 14 | 15 | // Marquee represents a marquee text than can be displayed over a media 16 | // instance, along with its visual properties. 17 | // 18 | // For more information see https://wiki.videolan.org/Documentation:Modules/marq. 19 | type Marquee struct { 20 | player *Player 21 | } 22 | 23 | func newMarquee(player *Player) *Marquee { 24 | return &Marquee{ 25 | player: player, 26 | } 27 | } 28 | 29 | // Enable enables or disables the marquee. By default, the marquee is disabled. 30 | func (m *Marquee) Enable(enable bool) error { 31 | return m.setInt(C.libvlc_marquee_Enable, boolToInt(enable)) 32 | } 33 | 34 | // Text returns the marquee text. 35 | // Default: "". 36 | func (m *Marquee) Text() (string, error) { 37 | return m.getString(C.libvlc_marquee_Text) 38 | } 39 | 40 | // SetText sets the marquee text. 41 | // The specified text can contain time format string sequences which are 42 | // converted to the requested time values at runtime. Most of the time 43 | // conversion specifiers supported by the `strftime` C function can be used. 44 | // 45 | // Common time format string sequences: 46 | // %Y = year, %m = month, %d = day, %H = hour, %M = minute, %S = second. 47 | // For more information see https://en.cppreference.com/w/c/chrono/strftime. 48 | func (m *Marquee) SetText(text string) error { 49 | return m.setString(C.libvlc_marquee_Text, text) 50 | } 51 | 52 | // Color returns the marquee text color. 53 | // Opacity information is included in the returned color. 54 | // Default: white. 55 | func (m *Marquee) Color() (color.Color, error) { 56 | // Get color. 57 | rgb, err := m.getInt(C.libvlc_marquee_Color) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | // Get alpha. 63 | alpha, err := m.getInt(C.libvlc_marquee_Opacity) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | return color.RGBA{ 69 | R: uint8((rgb >> 16) & 0x0ff), 70 | G: uint8((rgb >> 8) & 0x0ff), 71 | B: uint8((rgb) & 0x0ff), 72 | A: uint8(alpha & 0x0ff), 73 | }, err 74 | } 75 | 76 | // SetColor sets the color of the marquee text. The opacity of the text 77 | // is also set, based on the alpha value of the color. 78 | func (m *Marquee) SetColor(color color.Color) error { 79 | r, g, b, a := color.RGBA() 80 | 81 | // Set color. 82 | rgb := int((((r >> 8) & 0x0ff) << 16) | 83 | (((g >> 8) & 0x0ff) << 8) | 84 | ((b >> 8) & 0x0ff)) 85 | if err := m.setInt(C.libvlc_marquee_Color, rgb); err != nil { 86 | return err 87 | } 88 | 89 | // Set alpha. 90 | alpha := int((a >> 8) & 0x0ff) 91 | if err := m.setInt(C.libvlc_marquee_Opacity, alpha); err != nil { 92 | return err 93 | } 94 | 95 | return nil 96 | } 97 | 98 | // Opacity returns the opacity of the marquee text. 99 | // The returned opacity is a value between 0 (transparent) and 255 (opaque). 100 | // Default: 255. 101 | func (m *Marquee) Opacity() (int, error) { 102 | return m.getInt(C.libvlc_marquee_Opacity) 103 | } 104 | 105 | // SetOpacity sets the opacity of the marquee text. The opacity is specified 106 | // as an integer between 0 (transparent) and 255 (opaque). 107 | func (m *Marquee) SetOpacity(opacity int) error { 108 | return m.setInt(C.libvlc_marquee_Opacity, opacity) 109 | } 110 | 111 | // Position returns the position of the marquee, relative to its container. 112 | // Default: vlc.PositionTopLeft. 113 | func (m *Marquee) Position() (Position, error) { 114 | iVal, err := m.getInt(C.libvlc_marquee_Position) 115 | if err != nil { 116 | return PositionDisable, err 117 | } 118 | 119 | switch { 120 | case iVal >= 8: 121 | iVal -= 2 122 | case iVal >= 4: 123 | iVal-- 124 | } 125 | 126 | if iVal < 0 { 127 | return PositionTopLeft, nil 128 | } 129 | return Position(iVal), nil 130 | } 131 | 132 | // SetPosition sets the position of the marquee, relative to its container. 133 | func (m *Marquee) SetPosition(position Position) error { 134 | switch { 135 | case position >= PositionBottom: 136 | position += 2 137 | case position >= PositionTop: 138 | position++ 139 | } 140 | 141 | return m.setInt(C.libvlc_marquee_Position, int(position)) 142 | } 143 | 144 | // X returns the X coordinate of the marquee text. The returned value is 145 | // relative to the position of the marquee inside its container, i.e. the 146 | // position set using the `Marquee.SetPosition` method. 147 | // Default: 0. 148 | func (m *Marquee) X() (int, error) { 149 | return m.getInt(C.libvlc_marquee_X) 150 | } 151 | 152 | // SetX sets the X coordinate of the marquee text. The value is specified 153 | // relative to the position of the marquee inside its container. 154 | // 155 | // NOTE: the method has no effect if the position of the marquee is set to 156 | // `vlc.PositionCenter`, `vlc.PositionTop` or `vlc.PositionBottom`. 157 | func (m *Marquee) SetX(x int) error { 158 | return m.setInt(C.libvlc_marquee_X, x) 159 | } 160 | 161 | // Y returns the Y coordinate of the marquee text. The returned value is 162 | // relative to the position of the marquee inside its container, i.e. the 163 | // position set using the `Marquee.SetPosition` method. 164 | // Default: 0. 165 | func (m *Marquee) Y() (int, error) { 166 | return m.getInt(C.libvlc_marquee_Y) 167 | } 168 | 169 | // SetY sets the Y coordinate of the marquee text. The value is specified 170 | // relative to the position of the marquee inside its container. 171 | // 172 | // NOTE: the method has no effect if the position of the marquee is set to 173 | // `vlc.PositionCenter`, `vlc.PositionLeft` or `vlc.PositionRight`. 174 | func (m *Marquee) SetY(y int) error { 175 | return m.setInt(C.libvlc_marquee_Y, y) 176 | } 177 | 178 | // Size returns the font size used to render the marquee text. 179 | // Default: 0 (default font size is used). 180 | func (m *Marquee) Size() (int, error) { 181 | return m.getInt(C.libvlc_marquee_Size) 182 | } 183 | 184 | // SetSize sets the font size used to render the marquee text. 185 | func (m *Marquee) SetSize(size int) error { 186 | return m.setInt(C.libvlc_marquee_Size, size) 187 | } 188 | 189 | // RefreshInterval returns the interval between marquee text updates. 190 | // The marquee text refreshes mainly when using time format string sequences. 191 | // Default: 1s. 192 | func (m *Marquee) RefreshInterval() (time.Duration, error) { 193 | iVal, err := m.getInt(C.libvlc_marquee_Refresh) 194 | return time.Duration(iVal) * time.Millisecond, err 195 | } 196 | 197 | // SetRefreshInterval sets the interval between marquee text updates. 198 | // The marquee text refreshes mainly when using time format string sequences. 199 | func (m *Marquee) SetRefreshInterval(refreshInterval time.Duration) error { 200 | return m.setInt(C.libvlc_marquee_Refresh, int(refreshInterval.Milliseconds())) 201 | } 202 | 203 | // DisplayDuration returns the duration for which the marquee text 204 | // is set to be displayed. 205 | // Default: 0 (the marquee is displayed indefinitely). 206 | func (m *Marquee) DisplayDuration() (time.Duration, error) { 207 | iVal, err := m.getInt(C.libvlc_marquee_Timeout) 208 | return time.Duration(iVal) * time.Millisecond, err 209 | } 210 | 211 | // SetDisplayDuration sets the duration for which to display the marquee text. 212 | func (m *Marquee) SetDisplayDuration(displayDuration time.Duration) error { 213 | return m.setInt(C.libvlc_marquee_Timeout, int(displayDuration.Milliseconds())) 214 | } 215 | 216 | func (m *Marquee) getInt(option C.uint) (int, error) { 217 | if err := m.player.assertInit(); err != nil { 218 | return 0, err 219 | } 220 | 221 | return int(C.libvlc_video_get_marquee_int(m.player.player, option)), nil 222 | } 223 | 224 | func (m *Marquee) setInt(option C.uint, val int) error { 225 | if err := m.player.assertInit(); err != nil { 226 | return err 227 | } 228 | 229 | C.libvlc_video_set_marquee_int(m.player.player, option, C.int(val)) 230 | return nil 231 | } 232 | 233 | func (m *Marquee) getString(option C.uint) (string, error) { 234 | if err := m.player.assertInit(); err != nil { 235 | return "", err 236 | } 237 | 238 | cVal := C.libvlc_video_get_marquee_string(m.player.player, option) 239 | if cVal == nil { 240 | return "", errOrDefault(getError(), ErrInvalid) 241 | } 242 | defer C.free(unsafe.Pointer(cVal)) 243 | 244 | return C.GoString(cVal), nil 245 | } 246 | 247 | func (m *Marquee) setString(option C.uint, val string) error { 248 | if err := m.player.assertInit(); err != nil { 249 | return err 250 | } 251 | 252 | cVal := C.CString(val) 253 | defer C.free(unsafe.Pointer(cVal)) 254 | 255 | C.libvlc_video_set_marquee_string(m.player.player, option, cVal) 256 | return nil 257 | } 258 | -------------------------------------------------------------------------------- /v2/media.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | // #include 6 | import "C" 7 | import ( 8 | "fmt" 9 | "os" 10 | "time" 11 | "unsafe" 12 | ) 13 | 14 | // MediaState represents the state of a media file. 15 | type MediaState uint 16 | 17 | // Media states. 18 | const ( 19 | MediaNothingSpecial MediaState = iota 20 | MediaOpening 21 | MediaBuffering 22 | MediaPlaying 23 | MediaPaused 24 | MediaStopped 25 | MediaEnded 26 | MediaError 27 | ) 28 | 29 | // MediaMetaKey uniquely identifies a type of media metadata. 30 | type MediaMetaKey uint 31 | 32 | // Media metadata types. 33 | const ( 34 | MediaTitle MediaMetaKey = iota 35 | MediaArtist 36 | MediaGenre 37 | MediaCopyright 38 | MediaAlbum 39 | MediaTrackNumber 40 | MediaDescription 41 | MediaRating 42 | MediaDate 43 | MediaSetting 44 | MediaURL 45 | MediaLanguage 46 | MediaNowPlaying 47 | MediaPublisher 48 | MediaEncodedBy 49 | MediaArtworkURL 50 | MediaTrackID 51 | MediaTrackTotal 52 | MediaDirector 53 | MediaSeason 54 | MediaEpisode 55 | MediaShowName 56 | MediaActors 57 | MediaAlbumArtist 58 | MediaDiscNumber 59 | MediaDiscTotal 60 | ) 61 | 62 | // Validate checks if the media metadata key is valid. 63 | func (mt MediaMetaKey) Validate() error { 64 | if mt > MediaDiscTotal { 65 | return ErrInvalid 66 | } 67 | 68 | return nil 69 | } 70 | 71 | // MediaStats contains playback statistics for a media file. 72 | type MediaStats struct { 73 | // Input statistics. 74 | ReadBytes int // Input bytes read. 75 | InputBitRate float64 // Input bit rate. 76 | 77 | // Demux statistics. 78 | DemuxReadBytes int // Demux bytes read (demuxed data size). 79 | DemuxBitRate float64 // Demux bit rate (content bit rate). 80 | DemuxCorrupted int // Demux corruptions (discarded). 81 | DemuxDiscontinuity int // Demux discontinuities (dropped). 82 | 83 | // Video output statistics. 84 | DecodedVideo int // Number of decoded video blocks. 85 | DisplayedPictures int // Number of displayed frames. 86 | LostPictures int // Number of lost frames. 87 | 88 | // Audio output statistics. 89 | DecodedAudio int // Number of decoded audio blocks. 90 | PlayedAudioBuffers int // Number of played audio buffers. 91 | LostAudioBuffers int // Number of lost audio buffers. 92 | } 93 | 94 | func newMediaStats(st *C.libvlc_media_stats_t) (*MediaStats, error) { 95 | if st == nil { 96 | return nil, ErrInvalidMediaStats 97 | } 98 | 99 | return &MediaStats{ 100 | // Input statistics. 101 | ReadBytes: int(st.i_read_bytes), 102 | InputBitRate: float64(st.f_input_bitrate), 103 | 104 | // Demux statistics. 105 | DemuxReadBytes: int(st.i_demux_read_bytes), 106 | DemuxBitRate: float64(st.f_demux_bitrate), 107 | DemuxCorrupted: int(st.i_demux_corrupted), 108 | DemuxDiscontinuity: int(st.i_demux_discontinuity), 109 | 110 | // Video output statistics. 111 | DecodedVideo: int(st.i_decoded_video), 112 | DisplayedPictures: int(st.i_displayed_pictures), 113 | LostPictures: int(st.i_lost_pictures), 114 | 115 | // Audio output statistics. 116 | DecodedAudio: int(st.i_decoded_audio), 117 | PlayedAudioBuffers: int(st.i_played_abuffers), 118 | LostAudioBuffers: int(st.i_lost_abuffers), 119 | }, nil 120 | } 121 | 122 | // MediaScreenOptions provides configuration options for creating media 123 | // instances from the current computer screen. 124 | type MediaScreenOptions struct { 125 | // Screen capture area. 126 | X int // Left edge coordinate of the subscreen. Default: 0. 127 | Y int // Top edge coordinate of the subscreen. Default: 0. 128 | Width int // Width of the subscreen. Default: 0 (full screen width). 129 | Height int // Height of the subscreen. Default: 0 (full screen height). 130 | 131 | // Screen capture frame rate. Default: 0. 132 | FPS float64 133 | 134 | // Follow the mouse when capturing a subscreen. Default: false. 135 | FollowMouse bool 136 | 137 | // Mouse cursor image to use. If specified, the cursor will be overlayed 138 | // on the captured video. Default: "". 139 | // NOTE: Windows only. 140 | CursorImage string 141 | 142 | // Optimize the capture by fragmenting the screen in chunks of predefined 143 | // height (16 might be a good value). Default: 0 (disabled). 144 | // NOTE: Windows only. 145 | FragmentSize int 146 | } 147 | 148 | type mediaData struct { 149 | userData interface{} 150 | } 151 | 152 | // Media is an abstract representation of a playable media file. 153 | type Media struct { 154 | media *C.libvlc_media_t 155 | } 156 | 157 | // NewMediaFromPath creates a new media instance based on the media 158 | // located at the specified path. 159 | func NewMediaFromPath(path string) (*Media, error) { 160 | return newMedia(path, true) 161 | } 162 | 163 | // NewMediaFromURL creates a new media instance based on the media 164 | // located at the specified URL. 165 | func NewMediaFromURL(url string) (*Media, error) { 166 | return newMedia(url, false) 167 | } 168 | 169 | // NewMediaFromScreen creates a media instance from the current computer 170 | // screen, using the specified options. 171 | // 172 | // NOTE: This functionality requires the VLC screen module to be installed. 173 | // See installation instructions at https://github.com/adrg/libvlc-go/wiki. 174 | // See https://wiki.videolan.org/Documentation:Modules/screen. 175 | func NewMediaFromScreen(opts *MediaScreenOptions) (*Media, error) { 176 | media, err := newMedia("screen://", false) 177 | if err != nil { 178 | return nil, err 179 | } 180 | if opts == nil { 181 | return media, nil 182 | } 183 | 184 | var mediaOpts []string 185 | if opts.X > 0 { 186 | mediaOpts = append(mediaOpts, fmt.Sprintf(":screen-left=%d", opts.X)) 187 | } 188 | if opts.Y > 0 { 189 | mediaOpts = append(mediaOpts, fmt.Sprintf(":screen-top=%d", opts.Y)) 190 | } 191 | if opts.Width > 0 { 192 | mediaOpts = append(mediaOpts, fmt.Sprintf(":screen-width=%d", opts.Width)) 193 | } 194 | if opts.Height > 0 { 195 | mediaOpts = append(mediaOpts, fmt.Sprintf(":screen-height=%d", opts.Height)) 196 | } 197 | if opts.FPS != 0 { 198 | mediaOpts = append(mediaOpts, fmt.Sprintf(":screen-fps=%f", opts.FPS)) 199 | } 200 | if opts.FollowMouse { 201 | mediaOpts = append(mediaOpts, ":screen-follow-mouse") 202 | } 203 | if opts.FragmentSize > 0 { 204 | mediaOpts = append(mediaOpts, fmt.Sprintf(":screen-fragment-size=%d", opts.FragmentSize)) 205 | } 206 | if opts.CursorImage != "" { 207 | mediaOpts = append(mediaOpts, fmt.Sprintf(":screen-mouse-image=%s", opts.CursorImage)) 208 | } 209 | 210 | if len(mediaOpts) > 0 { 211 | return media, media.AddOptions(mediaOpts...) 212 | } 213 | 214 | return media, nil 215 | } 216 | 217 | // Release destroys the media instance. 218 | func (m *Media) Release() error { 219 | if err := m.assertInit(); err != nil { 220 | return nil 221 | } 222 | 223 | m.release() 224 | return nil 225 | } 226 | 227 | // Duplicate duplicates the current media instance. 228 | // 229 | // NOTE: Call the Release method on the returned media in order to 230 | // free the allocated resources. 231 | func (m *Media) Duplicate() (*Media, error) { 232 | if err := m.assertInit(); err != nil { 233 | return nil, err 234 | } 235 | 236 | // Duplicate media. 237 | cMedia := C.libvlc_media_duplicate(m.media) 238 | if cMedia == nil { 239 | return nil, errOrDefault(getError(), ErrMediaCreate) 240 | } 241 | 242 | // Duplicate user data. 243 | dup := &Media{media: cMedia} 244 | if _, data := m.getUserData(); data != nil { 245 | dupData := *data 246 | dup.setUserData(&dupData) 247 | } 248 | 249 | return dup, nil 250 | } 251 | 252 | // AddOptions adds the specified options to the media. The specified options 253 | // determine how a media player reads the media, allowing advanced reading or 254 | // streaming on a per-media basis. 255 | func (m *Media) AddOptions(options ...string) error { 256 | if err := m.assertInit(); err != nil { 257 | return err 258 | } 259 | 260 | for _, option := range options { 261 | if err := m.addOption(option); err != nil { 262 | return err 263 | } 264 | } 265 | 266 | return nil 267 | } 268 | 269 | // State returns the current state of the media instance. 270 | func (m *Media) State() (MediaState, error) { 271 | if err := m.assertInit(); err != nil { 272 | return 0, err 273 | } 274 | 275 | return MediaState(C.libvlc_media_get_state(m.media)), nil 276 | } 277 | 278 | // Stats returns playback statistics for the media. 279 | func (m *Media) Stats() (*MediaStats, error) { 280 | if err := m.assertInit(); err != nil { 281 | return nil, err 282 | } 283 | 284 | var stats C.libvlc_media_stats_t 285 | if int(C.libvlc_media_get_stats(m.media, &stats)) != 1 { 286 | return nil, errOrDefault(getError(), ErrMissingMediaStats) 287 | } 288 | 289 | return newMediaStats(&stats) 290 | } 291 | 292 | // Location returns the media location, which can be either a local path or 293 | // a URL, depending on how the media was loaded. 294 | func (m *Media) Location() (string, error) { 295 | if err := m.assertInit(); err != nil { 296 | return "", err 297 | } 298 | 299 | mrl := C.libvlc_media_get_mrl(m.media) 300 | if mrl == nil { 301 | return "", ErrMissingMediaLocation 302 | } 303 | defer C.free(unsafe.Pointer(mrl)) 304 | 305 | return urlToPath(C.GoString(mrl)) 306 | } 307 | 308 | // Duration returns the media duration in milliseconds. 309 | // 310 | // NOTE: The duration can only be obtained for parsed media instances. 311 | // Either play the media once or call one of the parsing methods first. 312 | func (m *Media) Duration() (time.Duration, error) { 313 | if err := m.assertInit(); err != nil { 314 | return 0, err 315 | } 316 | 317 | duration := C.libvlc_media_get_duration(m.media) 318 | if duration < 0 { 319 | return 0, errOrDefault(getError(), ErrMediaNotParsed) 320 | } 321 | 322 | return time.Duration(duration) * time.Millisecond, nil 323 | } 324 | 325 | // Meta reads the value of the specified media metadata key. 326 | func (m *Media) Meta(key MediaMetaKey) (string, error) { 327 | if err := m.assertInit(); err != nil { 328 | return "", err 329 | } 330 | if err := key.Validate(); err != nil { 331 | return "", err 332 | } 333 | 334 | val := C.libvlc_media_get_meta(m.media, C.libvlc_meta_t(key)) 335 | if val == nil { 336 | return "", nil 337 | } 338 | defer C.free(unsafe.Pointer(val)) 339 | 340 | return C.GoString(val), nil 341 | } 342 | 343 | // SetMeta sets the specified media metadata key to the provided value. 344 | // In order to save the metadata on the media file, call SaveMeta. 345 | func (m *Media) SetMeta(key MediaMetaKey, val string) error { 346 | if err := m.assertInit(); err != nil { 347 | return err 348 | } 349 | if err := key.Validate(); err != nil { 350 | return err 351 | } 352 | 353 | cVal := C.CString(val) 354 | C.libvlc_media_set_meta(m.media, C.libvlc_meta_t(key), cVal) 355 | C.free(unsafe.Pointer(cVal)) 356 | return nil 357 | } 358 | 359 | // SaveMeta saves the previously set media metadata. 360 | func (m *Media) SaveMeta() error { 361 | if err := m.assertInit(); err != nil { 362 | return err 363 | } 364 | 365 | if int(C.libvlc_media_save_meta(m.media)) != 1 { 366 | return errOrDefault(getError(), ErrMediaMetaSave) 367 | } 368 | 369 | return nil 370 | } 371 | 372 | // Parse fetches local art, metadata and track information synchronously. 373 | func (m *Media) Parse() error { 374 | if err := m.assertInit(); err != nil { 375 | return err 376 | } 377 | 378 | C.libvlc_media_parse(m.media) 379 | return getError() 380 | } 381 | 382 | // ParseAsync fetches local art, metadata and track information asynchronously. 383 | // Listen to the MediaParsedChanged event on the media event manager the track 384 | // when the parsing has finished. However, if the media was already parsed, 385 | // the event is not sent. 386 | func (m *Media) ParseAsync() error { 387 | if err := m.assertInit(); err != nil { 388 | return err 389 | } 390 | 391 | C.libvlc_media_parse_async(m.media) 392 | return getError() 393 | } 394 | 395 | // IsParsed returns true if the media was parsed. 396 | func (m *Media) IsParsed() (bool, error) { 397 | if err := m.assertInit(); err != nil { 398 | return false, err 399 | } 400 | 401 | return C.libvlc_media_is_parsed(m.media) != 0, nil 402 | } 403 | 404 | // SubItems returns a media list containing the sub-items of the current 405 | // media instance. If the media does not have any sub-items, an empty media 406 | // list is returned. 407 | // 408 | // NOTE: Call the Release method on the returned media list in order to 409 | // free the allocated resources. 410 | func (m *Media) SubItems() (*MediaList, error) { 411 | if err := m.assertInit(); err != nil { 412 | return nil, err 413 | } 414 | 415 | var subitems *C.libvlc_media_list_t 416 | if subitems = C.libvlc_media_subitems(m.media); subitems == nil { 417 | return nil, errOrDefault(getError(), ErrMediaListNotFound) 418 | } 419 | 420 | return &MediaList{list: subitems}, nil 421 | } 422 | 423 | // Tracks returns the tracks (audio, video, subtitle) of the current media. 424 | // 425 | // NOTE: The tracks can only be obtained for parsed media instances. 426 | // Either play the media once or call one of the parsing methods first. 427 | func (m *Media) Tracks() ([]*MediaTrack, error) { 428 | if err := m.assertInit(); err != nil { 429 | return nil, err 430 | } 431 | 432 | // Get media tracks. 433 | var cTracks **C.libvlc_media_track_t 434 | 435 | count := int(C.libvlc_media_tracks_get(m.media, &cTracks)) 436 | if count <= 0 || cTracks == nil { 437 | return nil, nil 438 | } 439 | defer C.libvlc_media_tracks_release(cTracks, C.uint(count)) 440 | 441 | // Parse media tracks. 442 | tracks := make([]*MediaTrack, 0, count) 443 | for i := 0; i < count; i++ { 444 | // Get current track pointer. 445 | cTrack := unsafe.Pointer(uintptr(unsafe.Pointer(cTracks)) + 446 | uintptr(i)*unsafe.Sizeof(*cTracks)) 447 | if cTrack == nil { 448 | return nil, ErrMediaTrackNotInitialized 449 | } 450 | 451 | // Parse media track. 452 | track, err := parseMediaTrack(*(**C.libvlc_media_track_t)(cTrack)) 453 | if err != nil { 454 | return nil, err 455 | } 456 | 457 | tracks = append(tracks, track) 458 | } 459 | 460 | return tracks, nil 461 | } 462 | 463 | // UserData returns the user data associated with the media instance. 464 | // 465 | // NOTE: The method returns `nil` if no user data is found. 466 | func (m *Media) UserData() (interface{}, error) { 467 | if err := m.assertInit(); err != nil { 468 | return nil, err 469 | } 470 | 471 | // Retrieve user data. 472 | _, md := m.getUserData() 473 | if md == nil { 474 | return nil, nil 475 | } 476 | 477 | return md.userData, nil 478 | } 479 | 480 | // SetUserData associates the passed in user data with the media instance. 481 | // The data can be retrieved by using the UserData method. 482 | func (m *Media) SetUserData(userData interface{}) error { 483 | if err := m.assertInit(); err != nil { 484 | return err 485 | } 486 | 487 | // Set or update user data. 488 | if _, md := m.getUserData(); md != nil { 489 | md.userData = userData 490 | } else { 491 | m.setUserData(&mediaData{userData: userData}) 492 | } 493 | 494 | return nil 495 | } 496 | 497 | // EventManager returns the event manager responsible for the media. 498 | func (m *Media) EventManager() (*EventManager, error) { 499 | if err := m.assertInit(); err != nil { 500 | return nil, err 501 | } 502 | 503 | manager := C.libvlc_media_event_manager(m.media) 504 | if manager == nil { 505 | return nil, ErrMissingEventManager 506 | } 507 | 508 | return newEventManager(manager), nil 509 | } 510 | 511 | func (m *Media) addOption(option string) error { 512 | if option == "" { 513 | return nil 514 | } 515 | 516 | cOption := C.CString(option) 517 | defer C.free(unsafe.Pointer(cOption)) 518 | 519 | C.libvlc_media_add_option(m.media, cOption) 520 | return getError() 521 | } 522 | 523 | func (m *Media) getUserData() (objectID, *mediaData) { 524 | if err := inst.assertInit(); err != nil { 525 | return nil, nil 526 | } 527 | id := C.libvlc_media_get_user_data(m.media) 528 | 529 | obj, ok := inst.objects.get(id) 530 | if !ok { 531 | return nil, nil 532 | } 533 | 534 | data, ok := obj.(*mediaData) 535 | if !ok { 536 | return nil, nil 537 | } 538 | 539 | return id, data 540 | } 541 | 542 | func (m *Media) setUserData(data *mediaData) objectID { 543 | id := inst.objects.add(data) 544 | C.libvlc_media_set_user_data(m.media, id) 545 | return id 546 | } 547 | 548 | func (m *Media) deleteUserData() { 549 | id, data := m.getUserData() 550 | if data == nil { 551 | return 552 | } 553 | 554 | inst.objects.decRefs(id) 555 | } 556 | 557 | func (m *Media) release() { 558 | // Delete user data. 559 | m.deleteUserData() 560 | 561 | // Delete media. 562 | C.libvlc_media_release(m.media) 563 | m.media = nil 564 | } 565 | 566 | func (m *Media) assertInit() error { 567 | if m == nil || m.media == nil { 568 | return ErrMediaNotInitialized 569 | } 570 | 571 | return nil 572 | } 573 | 574 | func newMedia(path string, local bool) (*Media, error) { 575 | if err := inst.assertInit(); err != nil { 576 | return nil, err 577 | } 578 | 579 | cPath := C.CString(path) 580 | defer C.free(unsafe.Pointer(cPath)) 581 | 582 | var media *C.libvlc_media_t 583 | if local { 584 | if _, err := os.Stat(path); err != nil { 585 | return nil, err 586 | } 587 | 588 | media = C.libvlc_media_new_path(inst.handle, cPath) 589 | } else { 590 | media = C.libvlc_media_new_location(inst.handle, cPath) 591 | } 592 | 593 | if media == nil { 594 | return nil, errOrDefault(getError(), ErrMediaCreate) 595 | } 596 | 597 | return &Media{media: media}, nil 598 | } 599 | -------------------------------------------------------------------------------- /v2/media_list.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | import "C" 6 | 7 | // MediaList represents a collection of media files. 8 | type MediaList struct { 9 | list *C.libvlc_media_list_t 10 | } 11 | 12 | // NewMediaList creates an empty media list. 13 | func NewMediaList() (*MediaList, error) { 14 | if err := inst.assertInit(); err != nil { 15 | return nil, err 16 | } 17 | 18 | var list *C.libvlc_media_list_t 19 | if list = C.libvlc_media_list_new(inst.handle); list == nil { 20 | return nil, errOrDefault(getError(), ErrMediaListCreate) 21 | } 22 | 23 | return &MediaList{list: list}, nil 24 | } 25 | 26 | // Release destroys the media list instance. 27 | func (ml *MediaList) Release() error { 28 | if err := ml.assertInit(); err != nil { 29 | return nil 30 | } 31 | 32 | C.libvlc_media_list_release(ml.list) 33 | ml.list = nil 34 | 35 | return nil 36 | } 37 | 38 | // AddMedia adds the provided Media instance at the end of the media list. 39 | func (ml *MediaList) AddMedia(m *Media) error { 40 | if err := m.assertInit(); err != nil { 41 | return err 42 | } 43 | 44 | // Check if media list is read-only. 45 | isReadOnly, err := ml.IsReadOnly() 46 | if err != nil { 47 | return err 48 | } 49 | if isReadOnly { 50 | return ErrMediaListReadOnly 51 | } 52 | 53 | // Lock media list. 54 | if err := ml.Lock(); err != nil { 55 | return err 56 | } 57 | defer ml.unlock() 58 | 59 | // Add the media to the list. 60 | if C.libvlc_media_list_add_media(ml.list, m.media) < 0 { 61 | return errOrDefault(getError(), ErrMediaListActionFailed) 62 | } 63 | 64 | return nil 65 | } 66 | 67 | // AddMediaFromPath loads the media file at the specified path and adds it at 68 | // the end of the media list. 69 | func (ml *MediaList) AddMediaFromPath(path string) error { 70 | media, err := NewMediaFromPath(path) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | // Add the media to the list. 76 | if err := ml.AddMedia(media); err != nil { 77 | media.release() 78 | return err 79 | } 80 | 81 | return nil 82 | } 83 | 84 | // AddMediaFromURL loads the media file at the specified URL and adds it at 85 | // the end of the the media list. 86 | func (ml *MediaList) AddMediaFromURL(url string) error { 87 | media, err := NewMediaFromURL(url) 88 | if err != nil { 89 | return err 90 | } 91 | 92 | if err := ml.AddMedia(media); err != nil { 93 | media.release() 94 | return err 95 | } 96 | 97 | return nil 98 | } 99 | 100 | // InsertMedia inserts the provided Media instance in the list, 101 | // at the specified index. 102 | func (ml *MediaList) InsertMedia(m *Media, index uint) error { 103 | if err := m.assertInit(); err != nil { 104 | return err 105 | } 106 | 107 | // Check if media list is read-only. 108 | isReadOnly, err := ml.IsReadOnly() 109 | if err != nil { 110 | return err 111 | } 112 | if isReadOnly { 113 | return ErrMediaListReadOnly 114 | } 115 | 116 | // Lock media list. 117 | if err := ml.Lock(); err != nil { 118 | return err 119 | } 120 | defer ml.unlock() 121 | 122 | // Insert the media in the list. 123 | if C.libvlc_media_list_insert_media(ml.list, m.media, C.int(index)) < 0 { 124 | return errOrDefault(getError(), ErrMediaListActionFailed) 125 | } 126 | 127 | return nil 128 | } 129 | 130 | // InsertMediaFromPath loads the media file at the provided path and inserts 131 | // it in the list, at the specified index. 132 | func (ml *MediaList) InsertMediaFromPath(path string, index uint) error { 133 | media, err := NewMediaFromPath(path) 134 | if err != nil { 135 | return err 136 | } 137 | 138 | // Insert the media in the list. 139 | if err := ml.InsertMedia(media, index); err != nil { 140 | media.release() 141 | return err 142 | } 143 | 144 | return nil 145 | } 146 | 147 | // InsertMediaFromURL loads the media file at the provided URL and inserts 148 | // it in the list, at the specified index. 149 | func (ml *MediaList) InsertMediaFromURL(url string, index uint) error { 150 | media, err := NewMediaFromURL(url) 151 | if err != nil { 152 | return err 153 | } 154 | 155 | // Insert the media in the list. 156 | if err := ml.InsertMedia(media, index); err != nil { 157 | media.release() 158 | return err 159 | } 160 | 161 | return nil 162 | } 163 | 164 | // RemoveMediaAtIndex removes the media item at the specified index 165 | // from the list. 166 | func (ml *MediaList) RemoveMediaAtIndex(index uint) error { 167 | // Check if media list is read-only. 168 | isReadOnly, err := ml.IsReadOnly() 169 | if err != nil { 170 | return err 171 | } 172 | if isReadOnly { 173 | return ErrMediaListReadOnly 174 | } 175 | 176 | // Lock media list. 177 | if err := ml.Lock(); err != nil { 178 | return err 179 | } 180 | defer ml.unlock() 181 | 182 | // Remove the media from the list. 183 | if C.libvlc_media_list_remove_index(ml.list, C.int(index)) < 0 { 184 | return errOrDefault(getError(), ErrMediaListActionFailed) 185 | } 186 | 187 | return nil 188 | } 189 | 190 | // MediaAtIndex returns the media item at the specified index from the list. 191 | func (ml *MediaList) MediaAtIndex(index uint) (*Media, error) { 192 | // Lock media list. 193 | if err := ml.Lock(); err != nil { 194 | return nil, err 195 | } 196 | defer ml.unlock() 197 | 198 | // Retrieve the media at the specified index. 199 | media := C.libvlc_media_list_item_at_index(ml.list, C.int(index)) 200 | if media == nil { 201 | return nil, errOrDefault(getError(), ErrMediaListActionFailed) 202 | } 203 | 204 | // This call will not release the media. Instead, it will decrement 205 | // the reference count increased by libvlc_media_list_item_at_index. 206 | C.libvlc_media_release(media) 207 | 208 | return &Media{media}, nil 209 | } 210 | 211 | // IndexOfMedia returns the index of the specified media item in the list. 212 | // 213 | // NOTE: The same instance of a media item can be present multiple times 214 | // in the list. The method returns the first matched index. 215 | func (ml *MediaList) IndexOfMedia(m *Media) (int, error) { 216 | if err := m.assertInit(); err != nil { 217 | return 0, err 218 | } 219 | 220 | if err := ml.Lock(); err != nil { 221 | return 0, err 222 | } 223 | defer ml.unlock() 224 | 225 | // Retrieve the index of the media. 226 | idx := int(C.libvlc_media_list_index_of_item(ml.list, m.media)) 227 | if idx < 0 { 228 | return 0, errOrDefault(getError(), ErrMediaNotFound) 229 | } 230 | 231 | return idx, nil 232 | } 233 | 234 | // Count returns the number of media items in the list. 235 | func (ml *MediaList) Count() (int, error) { 236 | // Lock media list. 237 | if err := ml.Lock(); err != nil { 238 | return 0, err 239 | } 240 | defer ml.unlock() 241 | 242 | // Retrieve media count. 243 | return int(C.libvlc_media_list_count(ml.list)), nil 244 | } 245 | 246 | // IsReadOnly specifies if the media list can be modified. 247 | func (ml *MediaList) IsReadOnly() (bool, error) { 248 | if err := ml.assertInit(); err != nil { 249 | return false, err 250 | } 251 | 252 | return (C.libvlc_media_list_is_readonly(ml.list) != C.int(0)), nil 253 | } 254 | 255 | // AssociatedMedia returns the media instance associated with the list, 256 | // if one exists. A media instance is automatically associated with the 257 | // list of its sub-items. 258 | // 259 | // NOTE: Do not call Release on the returned media instance. 260 | func (ml *MediaList) AssociatedMedia() (*Media, error) { 261 | if err := ml.assertInit(); err != nil { 262 | return nil, err 263 | } 264 | 265 | media := C.libvlc_media_list_media(ml.list) 266 | if media == nil { 267 | return nil, errOrDefault(getError(), ErrMediaNotFound) 268 | } 269 | 270 | // This call will not release the media. Instead, it will decrement 271 | // the reference count increased by libvlc_media_list_media. 272 | C.libvlc_media_release(media) 273 | 274 | return &Media{media: media}, nil 275 | } 276 | 277 | // AssociateMedia associates the specified media with the media list instance. 278 | // 279 | // NOTE: If another media instance is already associated with the list, 280 | // it will be released. 281 | func (ml *MediaList) AssociateMedia(m *Media) error { 282 | if err := ml.assertInit(); err != nil { 283 | return err 284 | } 285 | if err := m.assertInit(); err != nil { 286 | return err 287 | } 288 | 289 | C.libvlc_media_list_set_media(ml.list, m.media) 290 | return nil 291 | } 292 | 293 | // Lock makes the caller the current owner of the media list. 294 | func (ml *MediaList) Lock() error { 295 | if err := ml.assertInit(); err != nil { 296 | return err 297 | } 298 | 299 | C.libvlc_media_list_lock(ml.list) 300 | return nil 301 | } 302 | 303 | // Unlock releases ownership of the media list. 304 | func (ml *MediaList) Unlock() error { 305 | if err := ml.assertInit(); err != nil { 306 | return err 307 | } 308 | 309 | ml.unlock() 310 | return nil 311 | } 312 | 313 | // EventManager returns the event manager responsible for the media list. 314 | func (ml *MediaList) EventManager() (*EventManager, error) { 315 | if err := ml.assertInit(); err != nil { 316 | return nil, err 317 | } 318 | 319 | manager := C.libvlc_media_list_event_manager(ml.list) 320 | if manager == nil { 321 | return nil, ErrMissingEventManager 322 | } 323 | 324 | return newEventManager(manager), nil 325 | } 326 | 327 | func (ml *MediaList) assertInit() error { 328 | if ml == nil || ml.list == nil { 329 | return ErrMediaListNotInitialized 330 | } 331 | 332 | return nil 333 | } 334 | 335 | func (ml *MediaList) unlock() { 336 | C.libvlc_media_list_unlock(ml.list) 337 | } 338 | -------------------------------------------------------------------------------- /v2/media_track.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | import "C" 6 | import ( 7 | "unsafe" 8 | ) 9 | 10 | // MediaTrackType represents the type of a media track. 11 | type MediaTrackType int 12 | 13 | // Media track types. 14 | const ( 15 | MediaTrackUnknown MediaTrackType = iota - 1 16 | MediaTrackAudio 17 | MediaTrackVideo 18 | MediaTrackText 19 | ) 20 | 21 | // MediaTrackDescriptor contains information about a media track. 22 | type MediaTrackDescriptor struct { 23 | ID int // Media track identifier. 24 | Description string // Description of the media track. 25 | } 26 | 27 | // MediaAudioTrack contains information specific to audio media tracks. 28 | type MediaAudioTrack struct { 29 | Channels uint // number of audio channels. 30 | Rate uint // audio sample rate. 31 | } 32 | 33 | // MediaVideoTrack contains information specific to video media tracks. 34 | type MediaVideoTrack struct { 35 | Width uint // video width. 36 | Height uint // video height. 37 | 38 | // Aspect ratio information. 39 | AspectRatioNum uint // aspect ratio numerator. 40 | AspectRatioDen uint // aspect ratio denominator. 41 | 42 | // Frame rate information. 43 | FrameRateNum uint // frame rate numerator. 44 | FrameRateDen uint // frame rate denominator. 45 | } 46 | 47 | // MediaSubtitleTrack contains information specific to subtitle media tracks. 48 | type MediaSubtitleTrack struct { 49 | Encoding string // character encoding of the subtitle. 50 | } 51 | 52 | // MediaTrack contains information regarding a media track. 53 | type MediaTrack struct { 54 | ID int // Media track identifier. 55 | Type MediaTrackType // Media track type. 56 | BitRate uint // Media track bit rate. 57 | 58 | // libVLC representation of the four-character code of the codec used by 59 | // the media track. 60 | Codec uint 61 | 62 | // The original four-character code of the codec used by the media track, 63 | // extracted from the container. 64 | OriginalCodec uint 65 | 66 | // Codec profile (real audio flavor, MPEG audio layer, H264 profile, etc.). 67 | // NOTE: Profile values are codec specific. 68 | Profile int 69 | 70 | // Stream restriction level (resolution, bitrate, codec features, etc.). 71 | // NOTE: Level values are codec specific. 72 | Level int 73 | 74 | Language string // Media track language name. 75 | Description string // Description of the media track. 76 | 77 | // Type specific information. 78 | Audio *MediaAudioTrack 79 | Video *MediaVideoTrack 80 | Subtitle *MediaSubtitleTrack 81 | } 82 | 83 | func (mt *MediaTrack) assertInit() error { 84 | if mt == nil { 85 | return ErrMediaTrackNotInitialized 86 | } 87 | 88 | return nil 89 | } 90 | 91 | func parseMediaTrack(cTrack *C.libvlc_media_track_t) (*MediaTrack, error) { 92 | if cTrack == nil { 93 | return nil, ErrMediaTrackNotInitialized 94 | } 95 | 96 | mt := &MediaTrack{ 97 | ID: int(cTrack.i_id), 98 | Type: MediaTrackType(cTrack.i_type), 99 | BitRate: uint(cTrack.i_bitrate), 100 | Codec: uint(cTrack.i_codec), 101 | OriginalCodec: uint(cTrack.i_original_fourcc), 102 | Profile: int(cTrack.i_profile), 103 | Level: int(cTrack.i_level), 104 | Language: C.GoString(cTrack.psz_language), 105 | Description: C.GoString(cTrack.psz_description), 106 | } 107 | 108 | switch mt.Type { 109 | case MediaTrackAudio: 110 | audio := *(**C.libvlc_audio_track_t)(unsafe.Pointer(&cTrack.anon0[0])) 111 | if audio == nil { 112 | break 113 | } 114 | 115 | mt.Audio = &MediaAudioTrack{ 116 | Channels: uint(audio.i_channels), 117 | Rate: uint(audio.i_rate), 118 | } 119 | case MediaTrackVideo: 120 | video := *(**C.libvlc_video_track_t)(unsafe.Pointer(&cTrack.anon0[0])) 121 | if video == nil { 122 | break 123 | } 124 | 125 | mt.Video = &MediaVideoTrack{ 126 | Width: uint(video.i_width), 127 | Height: uint(video.i_height), 128 | AspectRatioNum: uint(video.i_sar_num), 129 | AspectRatioDen: uint(video.i_sar_den), 130 | FrameRateNum: uint(video.i_frame_rate_num), 131 | FrameRateDen: uint(video.i_frame_rate_den), 132 | } 133 | case MediaTrackText: 134 | subtitle := *(**C.libvlc_subtitle_track_t)(unsafe.Pointer(&cTrack.anon0[0])) 135 | if subtitle == nil { 136 | break 137 | } 138 | 139 | mt.Subtitle = &MediaSubtitleTrack{ 140 | Encoding: C.GoString(subtitle.psz_encoding), 141 | } 142 | } 143 | 144 | return mt, nil 145 | } 146 | 147 | func parseMediaTrackDescriptorList(cDescriptors *C.libvlc_track_description_t) ([]*MediaTrackDescriptor, error) { 148 | if cDescriptors == nil { 149 | return nil, nil 150 | } 151 | 152 | var descriptors []*MediaTrackDescriptor 153 | for n := cDescriptors; n != nil; n = n.p_next { 154 | descriptors = append(descriptors, &MediaTrackDescriptor{ 155 | ID: int(n.i_id), 156 | Description: C.GoString(n.psz_name), 157 | }) 158 | } 159 | 160 | C.libvlc_track_description_list_release(cDescriptors) 161 | return descriptors, nil 162 | } 163 | -------------------------------------------------------------------------------- /v2/object_registry.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #include 4 | import "C" 5 | import ( 6 | "sync" 7 | "unsafe" 8 | ) 9 | 10 | type objectID = unsafe.Pointer 11 | 12 | type objectContext struct { 13 | refs uint 14 | data interface{} 15 | } 16 | 17 | type objectRegistry struct { 18 | sync.RWMutex 19 | 20 | contexts map[objectID]*objectContext 21 | } 22 | 23 | func newObjectRegistry() *objectRegistry { 24 | return &objectRegistry{ 25 | contexts: map[objectID]*objectContext{}, 26 | } 27 | } 28 | 29 | func (or *objectRegistry) get(id objectID) (interface{}, bool) { 30 | if id == nil { 31 | return nil, false 32 | } 33 | 34 | or.RLock() 35 | ctx, ok := or.contexts[id] 36 | or.RUnlock() 37 | 38 | if !ok { 39 | return nil, false 40 | } 41 | return ctx.data, ok 42 | } 43 | 44 | func (or *objectRegistry) add(data interface{}) objectID { 45 | or.Lock() 46 | 47 | var id objectID = C.malloc(C.size_t(1)) 48 | or.contexts[id] = &objectContext{ 49 | refs: 1, 50 | data: data, 51 | } 52 | 53 | or.Unlock() 54 | return id 55 | } 56 | 57 | func (or *objectRegistry) incRefs(id objectID) { 58 | if id == nil { 59 | return 60 | } 61 | 62 | or.Lock() 63 | 64 | ctx, ok := or.contexts[id] 65 | if ok { 66 | ctx.refs++ 67 | } 68 | 69 | or.Unlock() 70 | } 71 | 72 | func (or *objectRegistry) decRefs(id objectID) { 73 | if id == nil { 74 | return 75 | } 76 | 77 | or.Lock() 78 | 79 | ctx, ok := or.contexts[id] 80 | if ok { 81 | ctx.refs-- 82 | if ctx.refs == 0 { 83 | delete(or.contexts, id) 84 | C.free(id) 85 | } 86 | } 87 | 88 | or.Unlock() 89 | } 90 | -------------------------------------------------------------------------------- /v2/utils.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | // #include 6 | import "C" 7 | import ( 8 | "errors" 9 | "net/url" 10 | "path/filepath" 11 | "runtime" 12 | "strings" 13 | ) 14 | 15 | func getError() error { 16 | msg := C.libvlc_errmsg() 17 | if msg == nil { 18 | return nil 19 | } 20 | 21 | err := errors.New(C.GoString(msg)) 22 | C.libvlc_clearerr() 23 | return err 24 | } 25 | 26 | func errOrDefault(err, defaultErr error) error { 27 | if err != nil { 28 | return err 29 | } 30 | 31 | return defaultErr 32 | } 33 | 34 | func boolToInt(value bool) int { 35 | if value { 36 | return 1 37 | } 38 | 39 | return 0 40 | } 41 | 42 | func urlToPath(mrl string) (string, error) { 43 | url, err := url.Parse(mrl) 44 | if err != nil { 45 | return "", err 46 | } 47 | if url.Scheme != "file" { 48 | return mrl, nil 49 | } 50 | path := filepath.Clean(url.Path) 51 | 52 | if runtime.GOOS == "windows" { 53 | sep := string(filepath.Separator) 54 | if url.Host != "" { 55 | path = strings.Repeat(sep, 2) + filepath.Join(url.Host, path) 56 | } else { 57 | path = strings.TrimLeft(path, sep) 58 | } 59 | } 60 | 61 | return path, nil 62 | } 63 | -------------------------------------------------------------------------------- /v2/version.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | // #include 6 | import "C" 7 | import "fmt" 8 | 9 | // VersionInfo contains details regarding the version of the libVLC module. 10 | type VersionInfo struct { 11 | Major uint 12 | Minor uint 13 | Patch uint 14 | Extra uint 15 | } 16 | 17 | // String returns a string representation of the version. 18 | func (v VersionInfo) String() string { 19 | return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) 20 | } 21 | 22 | // Runtime returns the runtime version of libVLC, usually including 23 | // the codename of the build. 24 | // 25 | // NOTE: Due to binary backward compatibility, the runtime version may be 26 | // more recent than the build version. 27 | func (v VersionInfo) Runtime() string { 28 | return C.GoString(C.libvlc_get_version()) 29 | } 30 | 31 | // Changeset returns the changeset identifier for the current libVLC build. 32 | func (v VersionInfo) Changeset() string { 33 | return C.GoString(C.libvlc_get_changeset()) 34 | } 35 | 36 | // Compiler returns information regarding the compiler used to build libVLC. 37 | func (v VersionInfo) Compiler() string { 38 | return C.GoString(C.libvlc_get_compiler()) 39 | } 40 | 41 | var moduleVersion = VersionInfo{ 42 | Major: C.LIBVLC_VERSION_MAJOR, 43 | Minor: C.LIBVLC_VERSION_MINOR, 44 | Patch: C.LIBVLC_VERSION_REVISION, 45 | Extra: C.LIBVLC_VERSION_EXTRA, 46 | } 47 | -------------------------------------------------------------------------------- /v2/vlc.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #cgo CFLAGS: -w 5 | // #include 6 | // #include 7 | import "C" 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | type instance struct { 13 | handle *C.libvlc_instance_t 14 | events *eventRegistry 15 | objects *objectRegistry 16 | } 17 | 18 | func (i *instance) assertInit() error { 19 | if i == nil || i.handle == nil { 20 | return ErrModuleNotInitialized 21 | } 22 | 23 | return nil 24 | } 25 | 26 | var inst *instance 27 | 28 | // Init creates an instance of the libVLC module. 29 | // Must be called only once and the module instance must be released using 30 | // the Release function. 31 | func Init(args ...string) error { 32 | if inst != nil { 33 | return nil 34 | } 35 | 36 | argc := len(args) 37 | argv := make([]*C.char, argc) 38 | 39 | for i, arg := range args { 40 | argv[i] = C.CString(arg) 41 | } 42 | defer func() { 43 | for i := range argv { 44 | C.free(unsafe.Pointer(argv[i])) 45 | } 46 | }() 47 | 48 | handle := C.libvlc_new(C.int(argc), *(***C.char)(unsafe.Pointer(&argv))) 49 | if handle == nil { 50 | return errOrDefault(getError(), ErrModuleInitialize) 51 | } 52 | 53 | inst = &instance{ 54 | handle: handle, 55 | events: newEventRegistry(), 56 | objects: newObjectRegistry(), 57 | } 58 | 59 | return nil 60 | } 61 | 62 | // Release destroys the instance created by the Init function. 63 | func Release() error { 64 | if inst == nil { 65 | return nil 66 | } 67 | 68 | C.libvlc_release(inst.handle) 69 | inst = nil 70 | 71 | return nil 72 | } 73 | 74 | // Version returns details regarding the version of the libVLC module. 75 | func Version() VersionInfo { 76 | return moduleVersion 77 | } 78 | 79 | // SetAppName sets the human-readable application name and the HTTP user agent. 80 | // The specified user agent is used when a protocol requires it. 81 | func SetAppName(name, userAgent string) error { 82 | if err := inst.assertInit(); err != nil { 83 | return err 84 | } 85 | 86 | cName, cUserAgent := C.CString(name), C.CString(userAgent) 87 | C.libvlc_set_user_agent(inst.handle, cName, cUserAgent) 88 | 89 | C.free(unsafe.Pointer(cName)) 90 | C.free(unsafe.Pointer(cUserAgent)) 91 | return nil 92 | } 93 | 94 | // SetAppID sets metadata for identifying the application. 95 | func SetAppID(id, version, icon string) error { 96 | if err := inst.assertInit(); err != nil { 97 | return err 98 | } 99 | 100 | cID, cVersion, cIcon := C.CString(id), C.CString(version), C.CString(icon) 101 | C.libvlc_set_app_id(inst.handle, cID, cVersion, cIcon) 102 | 103 | C.free(unsafe.Pointer(cID)) 104 | C.free(unsafe.Pointer(cVersion)) 105 | C.free(unsafe.Pointer(cIcon)) 106 | return nil 107 | } 108 | 109 | // StartUserInterface attempts to start a user interface for the libVLC 110 | // instance. Pass an empty string as the name parameter in order to start 111 | // the default interface. 112 | func StartUserInterface(name string) error { 113 | if err := inst.assertInit(); err != nil { 114 | return err 115 | } 116 | 117 | cName := C.CString(name) 118 | defer C.free(unsafe.Pointer(cName)) 119 | 120 | if C.libvlc_add_intf(inst.handle, cName) < 0 { 121 | return errOrDefault(getError(), ErrUserInterfaceStart) 122 | } 123 | 124 | return nil 125 | } 126 | -------------------------------------------------------------------------------- /v3/av.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | // #include 6 | import "C" 7 | import ( 8 | "unsafe" 9 | ) 10 | 11 | // StereoMode defines stereo modes which can be used by an audio output. 12 | type StereoMode int 13 | 14 | // Stereo modes. 15 | const ( 16 | StereoModeError StereoMode = iota - 1 17 | StereoModeNotSet 18 | StereoModeNormal 19 | StereoModeReverse 20 | StereoModeLeft 21 | StereoModeRight 22 | StereoModeDolbySurround 23 | StereoModeHeadphones 24 | ) 25 | 26 | // Position defines locations of entities relative to a container. 27 | type Position int 28 | 29 | // Positions. 30 | const ( 31 | PositionDisable Position = iota - 1 32 | PositionCenter 33 | PositionLeft 34 | PositionRight 35 | PositionTop 36 | PositionTopLeft 37 | PositionTopRight 38 | PositionBottom 39 | PositionBottomLeft 40 | PositionBottomRight 41 | ) 42 | 43 | // DeinterlaceMode defines deinterlacing modes which can be used when 44 | // rendering videos. 45 | // 46 | // For more information see https://wiki.videolan.org/Deinterlacing. 47 | type DeinterlaceMode string 48 | 49 | // Deinterlace modes. 50 | const ( 51 | DeinterlaceModeDisable DeinterlaceMode = "" 52 | DeinterlaceModeDiscard DeinterlaceMode = "discard" 53 | DeinterlaceModeBlend DeinterlaceMode = "blend" 54 | DeinterlaceModeMean DeinterlaceMode = "mean" 55 | DeinterlaceModeBob DeinterlaceMode = "bob" 56 | DeinterlaceModeLinear DeinterlaceMode = "linear" 57 | DeinterlaceModeX DeinterlaceMode = "x" 58 | DeinterlaceModeYadif DeinterlaceMode = "yadif" 59 | DeinterlaceModeYadif2x DeinterlaceMode = "yadif2x" 60 | DeinterlaceModePhosphor DeinterlaceMode = "phosphor" 61 | DeinterlaceModeIVTC DeinterlaceMode = "ivtc" 62 | ) 63 | 64 | // AudioOutput contains information regarding an audio output. 65 | type AudioOutput struct { 66 | Name string 67 | Description string 68 | } 69 | 70 | // AudioOutputList returns the list of available audio outputs. 71 | // In order to change the audio output of a media player instance, 72 | // use the Player.SetAudioOutput method. 73 | func AudioOutputList() ([]*AudioOutput, error) { 74 | if err := inst.assertInit(); err != nil { 75 | return nil, err 76 | } 77 | 78 | cOutputs := C.libvlc_audio_output_list_get(inst.handle) 79 | if cOutputs == nil { 80 | return nil, errOrDefault(getError(), ErrAudioOutputListMissing) 81 | } 82 | 83 | var outputs []*AudioOutput 84 | for n := cOutputs; n != nil; n = n.p_next { 85 | outputs = append(outputs, &AudioOutput{ 86 | Name: C.GoString(n.psz_name), 87 | Description: C.GoString(n.psz_description), 88 | }) 89 | } 90 | 91 | C.libvlc_audio_output_list_release(cOutputs) 92 | return outputs, nil 93 | } 94 | 95 | // AudioOutputDevice contains information regarding an audio output device. 96 | type AudioOutputDevice struct { 97 | Name string 98 | Description string 99 | } 100 | 101 | // ListAudioOutputDevices returns the list of available devices for the 102 | // specified audio output. Use the AudioOutputList method in order to obtain 103 | // the list of available audio outputs. In order to change the audio output 104 | // device of a media player instance, use Player.SetAudioOutputDevice. 105 | // 106 | // NOTE: Not all audio outputs support this. An empty list of devices does 107 | // not imply that the specified audio output does not work. 108 | // Some audio output devices in the list might not work in some circumstances. 109 | // By default, it is recommended to not specify any explicit audio device. 110 | func ListAudioOutputDevices(output string) ([]*AudioOutputDevice, error) { 111 | if err := inst.assertInit(); err != nil { 112 | return nil, err 113 | } 114 | 115 | cOutput := C.CString(output) 116 | defer C.free(unsafe.Pointer(cOutput)) 117 | return parseAudioOutputDeviceList(C.libvlc_audio_output_device_list_get(inst.handle, cOutput)) 118 | } 119 | 120 | func parseAudioOutputDeviceList(cDevices *C.libvlc_audio_output_device_t) ([]*AudioOutputDevice, error) { 121 | if cDevices == nil { 122 | return nil, errOrDefault(getError(), ErrAudioOutputDeviceListMissing) 123 | } 124 | 125 | var devices []*AudioOutputDevice 126 | for n := cDevices; n != nil; n = n.p_next { 127 | devices = append(devices, &AudioOutputDevice{ 128 | Name: C.GoString(n.psz_device), 129 | Description: C.GoString(n.psz_description), 130 | }) 131 | } 132 | 133 | C.libvlc_audio_output_device_list_release(cDevices) 134 | return devices, nil 135 | } 136 | 137 | // ModuleDescription contains information about a libVLC module. 138 | type ModuleDescription struct { 139 | Name string 140 | ShortName string 141 | LongName string 142 | Help string 143 | } 144 | 145 | // ListAudioFilters returns the list of available audio filters. 146 | func ListAudioFilters() ([]*ModuleDescription, error) { 147 | if err := inst.assertInit(); err != nil { 148 | return nil, err 149 | } 150 | 151 | return parseFilterList(C.libvlc_audio_filter_list_get(inst.handle)) 152 | } 153 | 154 | // ListVideoFilters returns the list of available video filters. 155 | func ListVideoFilters() ([]*ModuleDescription, error) { 156 | if err := inst.assertInit(); err != nil { 157 | return nil, err 158 | } 159 | 160 | return parseFilterList(C.libvlc_video_filter_list_get(inst.handle)) 161 | } 162 | 163 | func parseFilterList(cFilters *C.libvlc_module_description_t) ([]*ModuleDescription, error) { 164 | if cFilters == nil { 165 | return nil, errOrDefault(getError(), ErrFilterListMissing) 166 | } 167 | 168 | var filters []*ModuleDescription 169 | for n := cFilters; n != nil; n = n.p_next { 170 | filters = append(filters, &ModuleDescription{ 171 | Name: C.GoString(n.psz_name), 172 | ShortName: C.GoString(n.psz_shortname), 173 | LongName: C.GoString(n.psz_longname), 174 | Help: C.GoString(n.psz_help), 175 | }) 176 | } 177 | 178 | C.libvlc_module_description_list_release(cFilters) 179 | return filters, nil 180 | } 181 | -------------------------------------------------------------------------------- /v3/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package vlc provides Golang bindings for libVLC version 3.X. 3 | 4 | # Usage 5 | 6 | Initialization 7 | 8 | // Initialize libVLC. Additional command line arguments can be passed in 9 | // to libVLC by specifying them in the Init function. 10 | if err := vlc.Init("--no-video", "--quiet"); err != nil { 11 | log.Fatal(err) 12 | } 13 | defer vlc.Release() 14 | 15 | Player example 16 | 17 | // Create a new player. 18 | player, err := vlc.NewPlayer() 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer func() { 23 | player.Stop() 24 | player.Release() 25 | }() 26 | 27 | // Add a media file from path or from URL. 28 | // Set player media from path: 29 | // media, err := player.LoadMediaFromPath("localpath/test.mp4") 30 | // Set player media from URL: 31 | media, err := player.LoadMediaFromURL("http://stream-uk1.radioparadise.com/mp3-32") 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | defer media.Release() 36 | 37 | // Retrieve player event manager. 38 | manager, err := player.EventManager() 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | // Register the media end reached event with the event manager. 44 | quit := make(chan struct{}) 45 | eventCallback := func(event vlc.Event, userData interface{}) { 46 | close(quit) 47 | } 48 | 49 | eventID, err := manager.Attach(vlc.MediaPlayerEndReached, eventCallback, nil) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | defer manager.Detach(eventID) 54 | 55 | // Start playing the media. 56 | if err = player.Play(); err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | <-quit 61 | 62 | List player example 63 | 64 | // Create a new list player. 65 | player, err := vlc.NewListPlayer() 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | defer func() { 70 | player.Stop() 71 | player.Release() 72 | }() 73 | 74 | // Create a new media list. 75 | list, err := vlc.NewMediaList() 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | defer list.Release() 80 | 81 | err = list.AddMediaFromPath("localpath/test1.mp3") 82 | if err != nil { 83 | log.Fatal(err) 84 | } 85 | 86 | err = list.AddMediaFromURL("http://stream-uk1.radioparadise.com/mp3-32") 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | 91 | // Set player media list. 92 | if err = player.SetMediaList(list); err != nil { 93 | log.Fatal(err) 94 | } 95 | 96 | // Media files can be added to the list after the list has been added 97 | // to the player. The player will play these files as well. 98 | err = list.AddMediaFromPath("localpath/test2.mp3") 99 | if err != nil { 100 | log.Fatal(err) 101 | } 102 | 103 | // Retrieve player event manager. 104 | manager, err := player.EventManager() 105 | if err != nil { 106 | log.Fatal(err) 107 | } 108 | 109 | // Register the media end reached event with the event manager. 110 | quit := make(chan struct{}) 111 | eventCallback := func(event vlc.Event, userData interface{}) { 112 | close(quit) 113 | } 114 | 115 | eventID, err := manager.Attach(vlc.MediaListPlayerPlayed, eventCallback, nil) 116 | if err != nil { 117 | log.Fatal(err) 118 | } 119 | defer manager.Detach(eventID) 120 | 121 | // Start playing the media list. 122 | if err = player.Play(); err != nil { 123 | log.Fatal(err) 124 | } 125 | 126 | <-quit 127 | 128 | Handling multiple events example 129 | 130 | // Create a new player. 131 | player, err := vlc.NewPlayer() 132 | if err != nil { 133 | log.Fatal(err) 134 | } 135 | defer func() { 136 | player.Stop() 137 | player.Release() 138 | }() 139 | 140 | // Add a media file from path or from URL. 141 | // Set player media from path: 142 | // media, err := player.LoadMediaFromPath("test.mp3") 143 | // Set player media from URL: 144 | media, err := player.LoadMediaFromURL("http://stream-uk1.radioparadise.com/mp3-32") 145 | if err != nil { 146 | log.Fatal(err) 147 | } 148 | defer media.Release() 149 | 150 | // Retrieve player event manager. 151 | manager, err := player.EventManager() 152 | if err != nil { 153 | log.Fatal(err) 154 | } 155 | 156 | // Create event handler. 157 | quit := make(chan struct{}) 158 | eventCallback := func(event vlc.Event, userData interface{}) { 159 | switch event { 160 | case vlc.MediaPlayerEndReached: 161 | log.Println("Player end reached") 162 | close(quit) 163 | case vlc.MediaPlayerTimeChanged: 164 | media, err := player.Media() 165 | if err != nil { 166 | log.Println(err) 167 | break 168 | } 169 | 170 | stats, err := media.Stats() 171 | if err != nil { 172 | log.Println(err) 173 | break 174 | } 175 | 176 | log.Printf("%+v\n", stats) 177 | } 178 | } 179 | 180 | // Register events with the event manager. 181 | events := []vlc.Event{ 182 | vlc.MediaPlayerTimeChanged, 183 | vlc.MediaPlayerEndReached, 184 | } 185 | 186 | var eventIDs []vlc.EventID 187 | for _, event := range events { 188 | eventID, err := manager.Attach(event, eventCallback, nil) 189 | if err != nil { 190 | log.Fatal(err) 191 | } 192 | 193 | eventIDs = append(eventIDs, eventID) 194 | } 195 | 196 | // De-register attached events. 197 | defer func() { 198 | for _, eventID := range eventIDs { 199 | manager.Detach(eventID) 200 | } 201 | }() 202 | 203 | // Start playing the media. 204 | if err = player.Play(); err != nil { 205 | log.Fatal(err) 206 | } 207 | 208 | <-quit 209 | */ 210 | package vlc 211 | -------------------------------------------------------------------------------- /v3/equalizer.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | import "C" 6 | 7 | // EqualizerPresetCount returns the number of available equalizer presets. 8 | func EqualizerPresetCount() uint { 9 | return uint(C.libvlc_audio_equalizer_get_preset_count()) 10 | } 11 | 12 | // EqualizerPresetName returns the name of the equalizer preset with the 13 | // specified index. The index must be a number greater than or equal to 0 14 | // and less than EqualizerPresetCount(). The function returns an empty string 15 | // for invalid indices. 16 | func EqualizerPresetName(index uint) string { 17 | return C.GoString(C.libvlc_audio_equalizer_get_preset_name(C.uint(index))) 18 | } 19 | 20 | // EqualizerPresetNames returns the names of all available equalizer presets, 21 | // sorted by their indices in ascending order. 22 | func EqualizerPresetNames() []string { 23 | // Get preset count. 24 | count := EqualizerPresetCount() 25 | 26 | // Get preset names. 27 | names := make([]string, 0, count) 28 | for i := uint(0); i < count; i++ { 29 | names = append(names, EqualizerPresetName(i)) 30 | } 31 | 32 | return names 33 | } 34 | 35 | // EqualizerBandCount returns the number of distinct equalizer frequency bands. 36 | func EqualizerBandCount() uint { 37 | return uint(C.libvlc_audio_equalizer_get_band_count()) 38 | } 39 | 40 | // EqualizerBandFrequency returns the frequency of the equalizer band with the 41 | // specified index. The index must be a number greater than or equal to 0 and 42 | // less than EqualizerBandCount(). The function returns -1 for invalid indices. 43 | func EqualizerBandFrequency(index uint) float64 { 44 | return float64(C.libvlc_audio_equalizer_get_band_frequency(C.uint(index))) 45 | } 46 | 47 | // EqualizerBandFrequencies returns the frequencies of all available equalizer 48 | // bands, sorted by their indices in ascending order. 49 | func EqualizerBandFrequencies() []float64 { 50 | // Get band count. 51 | count := EqualizerBandCount() 52 | 53 | // Get band frequencies. 54 | frequencies := make([]float64, 0, count) 55 | for i := uint(0); i < count; i++ { 56 | frequencies = append(frequencies, EqualizerBandFrequency(i)) 57 | } 58 | 59 | return frequencies 60 | } 61 | 62 | // Equalizer represents an audio equalizer. Use Player.SetEqualizer to assign 63 | // the equalizer to a player instance. 64 | type Equalizer struct { 65 | equalizer *C.libvlc_equalizer_t 66 | } 67 | 68 | // NewEqualizer returns a new equalizer with all frequency values set to zero. 69 | func NewEqualizer() (*Equalizer, error) { 70 | equalizer := C.libvlc_audio_equalizer_new() 71 | if equalizer == nil { 72 | return nil, errOrDefault(getError(), ErrEqualizerCreate) 73 | } 74 | 75 | return &Equalizer{equalizer: equalizer}, nil 76 | } 77 | 78 | // NewEqualizerFromPreset returns a new equalizer with the frequency values 79 | // copied from the preset with the specified index. The index must be a number 80 | // greater than or equal to 0 and less than EqualizerPresetCount(). 81 | func NewEqualizerFromPreset(index uint) (*Equalizer, error) { 82 | equalizer := C.libvlc_audio_equalizer_new_from_preset(C.uint(index)) 83 | if equalizer == nil { 84 | return nil, errOrDefault(getError(), ErrEqualizerCreate) 85 | } 86 | 87 | return &Equalizer{equalizer: equalizer}, nil 88 | } 89 | 90 | // Release destroys the equalizer instance. 91 | func (e *Equalizer) Release() error { 92 | if err := e.assertInit(); err != nil { 93 | return nil 94 | } 95 | 96 | C.libvlc_audio_equalizer_release(e.equalizer) 97 | e.equalizer = nil 98 | return nil 99 | } 100 | 101 | // PreampValue returns the pre-amplification value of the equalizer in Hz. 102 | func (e *Equalizer) PreampValue() (float64, error) { 103 | if err := e.assertInit(); err != nil { 104 | return 0, err 105 | } 106 | 107 | value := C.libvlc_audio_equalizer_get_preamp(e.equalizer) 108 | return float64(value), nil 109 | } 110 | 111 | // SetPreampValue sets the pre-amplification value of the equalizer. 112 | // The specified amplification value is clamped to the [-20.0, 20.0] Hz range. 113 | func (e *Equalizer) SetPreampValue(value float64) error { 114 | if err := e.assertInit(); err != nil { 115 | return err 116 | } 117 | 118 | if C.libvlc_audio_equalizer_set_preamp(e.equalizer, C.float(value)) != 0 { 119 | return errOrDefault(getError(), ErrEqualizerAmpValueSet) 120 | } 121 | 122 | return nil 123 | } 124 | 125 | // AmpValueAtIndex returns the amplification value for the equalizer frequency 126 | // band with the specified index, in Hz. The index must be a number greater 127 | // than or equal to 0 and less than EqualizerBandCount(). 128 | func (e *Equalizer) AmpValueAtIndex(index uint) (float64, error) { 129 | if err := e.assertInit(); err != nil { 130 | return 0, err 131 | } 132 | 133 | value := C.libvlc_audio_equalizer_get_amp_at_index(e.equalizer, C.uint(index)) 134 | return float64(value), nil 135 | } 136 | 137 | // SetAmpValueAtIndex sets the amplification value for the equalizer frequency 138 | // band with the specified index, in Hz. The index must be a number greater 139 | // than or equal to 0 and less than EqualizerBandCount(). 140 | func (e *Equalizer) SetAmpValueAtIndex(value float64, index uint) error { 141 | if err := e.assertInit(); err != nil { 142 | return err 143 | } 144 | 145 | if C.libvlc_audio_equalizer_set_amp_at_index(e.equalizer, C.float(value), C.uint(index)) != 0 { 146 | return errOrDefault(getError(), ErrEqualizerAmpValueSet) 147 | } 148 | 149 | return nil 150 | } 151 | 152 | func (e *Equalizer) assertInit() error { 153 | if e == nil || e.equalizer == nil { 154 | return ErrEqualizerNotInitialized 155 | } 156 | 157 | return nil 158 | } 159 | -------------------------------------------------------------------------------- /v3/errors.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | import "errors" 4 | 5 | // Generic errors. 6 | var ( 7 | ErrInvalid = errors.New("the provided value is not valid") 8 | ) 9 | 10 | // Module errors. 11 | var ( 12 | ErrModuleInitialize = errors.New("could not initialize module") 13 | ErrModuleNotInitialized = errors.New("module is not initialized") 14 | ErrUserInterfaceStart = errors.New("could not start user interface") 15 | ) 16 | 17 | // Player errors. 18 | var ( 19 | ErrPlayerCreate = errors.New("could not create player") 20 | ErrPlayerNotInitialized = errors.New("player is not initialized") 21 | ErrPlayerPlay = errors.New("cannot play the requested media") 22 | ErrPlayerSetVolume = errors.New("could not set player volume") 23 | ErrPlayerSetRenderer = errors.New("could not set player renderer") 24 | ErrPlayerSetEqualizer = errors.New("could not set player equalizer") 25 | ErrPlayerInvalidRole = errors.New("invalid player role") 26 | ErrPlayerTitleNotInitialized = errors.New("player title not initialized") 27 | ErrPlayerChapterNotInitialized = errors.New("player chapter not initialized") 28 | ) 29 | 30 | // List player errors. 31 | var ( 32 | ErrListPlayerCreate = errors.New("could not create list player") 33 | ErrListPlayerNotInitialized = errors.New("list player not initialized") 34 | ) 35 | 36 | // Media errors. 37 | var ( 38 | ErrMediaCreate = errors.New("could not create media") 39 | ErrMediaNotFound = errors.New("could not find media") 40 | ErrMediaNotInitialized = errors.New("media is not initialized") 41 | ErrMediaListCreate = errors.New("could not create media list") 42 | ErrMediaListNotFound = errors.New("could not find media list") 43 | ErrMediaListNotInitialized = errors.New("media list is not initialized") 44 | ErrMediaListReadOnly = errors.New("media list is read-only") 45 | ErrMediaListActionFailed = errors.New("could not perform media list action") 46 | ErrMissingMediaStats = errors.New("could not get media statistics") 47 | ErrInvalidMediaStats = errors.New("invalid media statistics") 48 | ErrMissingMediaLocation = errors.New("could not get media location") 49 | ErrMissingMediaDimensions = errors.New("could not get media dimensions") 50 | ErrMediaMetaSave = errors.New("could not save media metadata") 51 | ErrMediaParse = errors.New("could not parse media") 52 | ErrMediaNotParsed = errors.New("media is not parsed") 53 | ) 54 | 55 | // Media track errors. 56 | var ( 57 | ErrMediaTrackNotInitialized = errors.New("media track is not initialized") 58 | ErrMediaTrackNotFound = errors.New("could not find media track") 59 | ErrInvalidMediaTrack = errors.New("invalid media track") 60 | ) 61 | 62 | // Event manager errors. 63 | var ( 64 | ErrMissingEventManager = errors.New("could not get event manager instance") 65 | ErrInvalidEventCallback = errors.New("invalid event callback") 66 | ErrEventAttach = errors.New("could not attach event") 67 | ) 68 | 69 | // Audio/Video errors. 70 | var ( 71 | ErrAudioOutputListMissing = errors.New("could not get audio output list") 72 | ErrAudioOutputSet = errors.New("could not set audio output") 73 | ErrAudioOutputDeviceListMissing = errors.New("could not get audio output device list") 74 | ErrAudioOutputDeviceMissing = errors.New("could not get audio output device") 75 | ErrFilterListMissing = errors.New("could not get filter list") 76 | ErrStereoModeSet = errors.New("could not set stereo mode") 77 | ErrVideoViewpointSet = errors.New("could not set video viewpoint") 78 | ErrVideoSnapshot = errors.New("could not take video snapshot") 79 | ErrCursorPositionMissing = errors.New("could not get cursor position") 80 | ) 81 | 82 | // Renderer discoverer errors. 83 | var ( 84 | ErrRendererDiscovererParse = errors.New("could not parse renderer discoverer") 85 | ErrRendererDiscovererCreate = errors.New("could not create renderer discoverer") 86 | ErrRendererDiscovererNotInitialized = errors.New("renderer discoverer not initialized") 87 | ErrRendererDiscovererStart = errors.New("could not start renderer discoverer") 88 | ErrRendererNotInitialized = errors.New("renderer not initialized") 89 | ) 90 | 91 | // Media discoverer errors. 92 | var ( 93 | ErrMediaDiscovererParse = errors.New("could not parse media discoverer") 94 | ErrMediaDiscovererCreate = errors.New("could not create media discoverer") 95 | ErrMediaDiscovererNotInitialized = errors.New("media discoverer not initialized") 96 | ErrMediaDiscovererStart = errors.New("could not start media discoverer") 97 | ) 98 | 99 | // Equalizer errors. 100 | var ( 101 | ErrEqualizerCreate = errors.New("could not create equalizer") 102 | ErrEqualizerNotInitialized = errors.New("equalizer not initialized") 103 | ErrEqualizerAmpValueSet = errors.New("could not set equalizer amplification value") 104 | ) 105 | -------------------------------------------------------------------------------- /v3/event_manager.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | /* 4 | #cgo LDFLAGS: -lvlc 5 | #include 6 | 7 | typedef const libvlc_event_t constev; 8 | extern void eventDispatch(constev*, void*); 9 | 10 | static inline int eventAttach(libvlc_event_manager_t* em, libvlc_event_type_t et, unsigned long userData) { 11 | return libvlc_event_attach(em, et, eventDispatch, (void*)userData); 12 | } 13 | static inline int eventDetach(libvlc_event_manager_t* em, libvlc_event_type_t et, unsigned long userData) { 14 | libvlc_event_detach(em, et, eventDispatch, (void*)userData); 15 | } 16 | */ 17 | import "C" 18 | 19 | import ( 20 | "unsafe" 21 | ) 22 | 23 | // EventManager wraps a libvlc event manager. 24 | type EventManager struct { 25 | manager *C.libvlc_event_manager_t 26 | } 27 | 28 | // newEventManager returns a new event manager instance. 29 | func newEventManager(manager *C.libvlc_event_manager_t) *EventManager { 30 | return &EventManager{ 31 | manager: manager, 32 | } 33 | } 34 | 35 | // Attach registers a callback for an event notification. 36 | func (em *EventManager) Attach(event Event, callback EventCallback, userData interface{}) (EventID, error) { 37 | return em.attach(event, callback, nil, userData) 38 | } 39 | 40 | // attach registers callbacks for an event notification. 41 | func (em *EventManager) attach(event Event, externalCallback EventCallback, 42 | internalCallback internalEventCallback, userData interface{}) (EventID, error) { 43 | if err := inst.assertInit(); err != nil { 44 | return 0, err 45 | } 46 | if externalCallback == nil && internalCallback == nil { 47 | return 0, ErrInvalidEventCallback 48 | } 49 | 50 | id := inst.events.add(event, externalCallback, internalCallback, userData) 51 | if C.eventAttach(em.manager, C.libvlc_event_type_t(event), C.ulong(id)) != 0 { 52 | return 0, errOrDefault(getError(), ErrEventAttach) 53 | } 54 | 55 | return id, nil 56 | } 57 | 58 | // Detach unregisters the specified event notification. 59 | func (em *EventManager) Detach(eventIDs ...EventID) { 60 | if err := inst.assertInit(); err != nil { 61 | return 62 | } 63 | 64 | for _, eventID := range eventIDs { 65 | ctx, ok := inst.events.get(eventID) 66 | if !ok { 67 | continue 68 | } 69 | 70 | inst.events.remove(eventID) 71 | C.eventDetach(em.manager, C.libvlc_event_type_t(ctx.event), C.ulong(eventID)) 72 | } 73 | } 74 | 75 | //export eventDispatch 76 | func eventDispatch(event *C.constev, userData unsafe.Pointer) { 77 | if err := inst.assertInit(); err != nil { 78 | return 79 | } 80 | 81 | ctx, ok := inst.events.get(EventID(uintptr(userData))) 82 | if !ok { 83 | return 84 | } 85 | 86 | // Execute external callback. 87 | if ctx.externalCallback != nil { 88 | ctx.externalCallback(ctx.event, ctx.userData) 89 | } 90 | 91 | // Execute internal callback. 92 | if ctx.internalCallback != nil { 93 | ctx.internalCallback(event, ctx.userData) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /v3/event_registry.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | import "C" 6 | import "sync" 7 | 8 | // EventID uniquely identifies a registered event. 9 | type EventID uint64 10 | 11 | // EventCallback represents an event notification callback function. 12 | type EventCallback func(Event, interface{}) 13 | 14 | type internalEventCallback func(*C.libvlc_event_t, interface{}) 15 | 16 | type eventContext struct { 17 | event Event 18 | externalCallback EventCallback 19 | internalCallback internalEventCallback 20 | userData interface{} 21 | } 22 | 23 | type eventRegistry struct { 24 | sync.RWMutex 25 | 26 | contexts map[EventID]*eventContext 27 | sequence EventID 28 | } 29 | 30 | func newEventRegistry() *eventRegistry { 31 | return &eventRegistry{ 32 | contexts: map[EventID]*eventContext{}, 33 | } 34 | } 35 | 36 | func (er *eventRegistry) get(id EventID) (*eventContext, bool) { 37 | if id == 0 { 38 | return nil, false 39 | } 40 | 41 | er.RLock() 42 | ctx, ok := er.contexts[id] 43 | er.RUnlock() 44 | 45 | return ctx, ok 46 | } 47 | 48 | func (er *eventRegistry) add(event Event, externalCallback EventCallback, 49 | internalCallback internalEventCallback, userData interface{}) EventID { 50 | er.Lock() 51 | 52 | er.sequence++ 53 | id := er.sequence 54 | 55 | er.contexts[id] = &eventContext{ 56 | event: event, 57 | externalCallback: externalCallback, 58 | internalCallback: internalCallback, 59 | userData: userData, 60 | } 61 | 62 | er.Unlock() 63 | return id 64 | } 65 | 66 | func (er *eventRegistry) remove(id EventID) { 67 | if id == 0 { 68 | return 69 | } 70 | 71 | er.Lock() 72 | delete(er.contexts, id) 73 | er.Unlock() 74 | } 75 | -------------------------------------------------------------------------------- /v3/events.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // Event represents an event that can occur inside libvlc. 4 | type Event int 5 | 6 | // Media events. 7 | const ( 8 | // MediaMetaChanged is triggered when the metadata of a media item changes. 9 | MediaMetaChanged Event = iota 10 | 11 | // MediaSubItemAdded is triggered when a Subitem is added to a media item. 12 | MediaSubItemAdded 13 | 14 | // MediaDurationChanged is triggered when the duration 15 | // of a media item changes. 16 | MediaDurationChanged 17 | 18 | // MediaParsedChanged is triggered when the parsing state 19 | // of a media item changes. 20 | MediaParsedChanged 21 | 22 | // MediaFreed is triggered when a media item is freed. 23 | MediaFreed 24 | 25 | // MediaStateChanged is triggered when the state of the media item changes. 26 | MediaStateChanged 27 | 28 | // MediaSubItemTreeAdded is triggered when a Subitem tree is 29 | // added to a media item. 30 | MediaSubItemTreeAdded 31 | 32 | // MediaThumbnailGenerated is triggered when a thumbnail 33 | // generation is completed. 34 | MediaThumbnailGenerated 35 | ) 36 | 37 | // Player events. 38 | const ( 39 | MediaPlayerMediaChanged Event = 0x100 + iota 40 | MediaPlayerNothingSpecial 41 | MediaPlayerOpening 42 | MediaPlayerBuffering 43 | MediaPlayerPlaying 44 | MediaPlayerPaused 45 | MediaPlayerStopped 46 | MediaPlayerForward 47 | MediaPlayerBackward 48 | MediaPlayerEndReached 49 | MediaPlayerEncounteredError 50 | MediaPlayerTimeChanged 51 | MediaPlayerPositionChanged 52 | MediaPlayerSeekableChanged 53 | MediaPlayerPausableChanged 54 | MediaPlayerTitleChanged 55 | MediaPlayerSnapshotTaken 56 | MediaPlayerLengthChanged 57 | MediaPlayerVout 58 | MediaPlayerScrambledChanged 59 | MediaPlayerESAdded 60 | MediaPlayerESDeleted 61 | MediaPlayerESSelected 62 | MediaPlayerCorked 63 | MediaPlayerUncorked 64 | MediaPlayerMuted 65 | MediaPlayerUnmuted 66 | MediaPlayerAudioVolume 67 | MediaPlayerAudioDevice 68 | MediaPlayerChapterChanged 69 | ) 70 | 71 | // Media list events. 72 | const ( 73 | // MediaListItemAdded is triggered when a media item is added to a media list. 74 | MediaListItemAdded Event = 0x200 + iota 75 | 76 | // MediaListWillAddItem is triggered when a media item is about to get 77 | // added to a media list. 78 | MediaListWillAddItem 79 | 80 | // MediaListItemDeleted is triggered when a media item is deleted 81 | // from a media list. 82 | MediaListItemDeleted 83 | 84 | // MediaListWillDeleteItem is triggered when a media item is about to get 85 | // deleted from a media list. 86 | MediaListWillDeleteItem 87 | 88 | // MediaListEndReached is triggered when a media list has reached the end. 89 | MediaListEndReached 90 | ) 91 | 92 | // Deprecated events. 93 | const ( 94 | MediaListViewItemAdded = 0x300 + iota 95 | MediaListViewWillAddItem 96 | MediaListViewItemDeleted 97 | MediaListViewWillDeleteItem 98 | ) 99 | 100 | const ( 101 | // MediaListPlayerPlayed is triggered when playback of the media list 102 | // of the list player has ended. 103 | MediaListPlayerPlayed = 0x400 + iota 104 | 105 | // MediaListPlayerNextItemSet is triggered when the current item 106 | // of a media list player has changed to a different item. 107 | MediaListPlayerNextItemSet 108 | 109 | // MediaListPlayerStopped is triggered when playback 110 | // of a media list player is stopped programmatically. 111 | MediaListPlayerStopped 112 | ) 113 | 114 | // Deprecated events. 115 | const ( 116 | MediaDiscovererStarted Event = 0x500 + iota 117 | MediaDiscovererEnded 118 | ) 119 | 120 | // Renderer events. 121 | const ( 122 | // RendererDiscovererItemAdded is triggered when a new renderer item is 123 | // found by a renderer discoverer. The renderer item is valid until deleted. 124 | RendererDiscovererItemAdded Event = 0x502 + iota 125 | 126 | // RendererDiscovererItemDeleted is triggered when a previously discovered 127 | // renderer item was deleted by a renderer discoverer. The renderer item 128 | // is no longer valid. 129 | RendererDiscovererItemDeleted 130 | ) 131 | 132 | // VideoLAN Manager events. 133 | const ( 134 | VlmMediaAdded Event = 0x600 + iota 135 | VlmMediaRemoved 136 | VlmMediaChanged 137 | VlmMediaInstanceStarted 138 | VlmMediaInstanceStopped 139 | VlmMediaInstanceStatusInit 140 | VlmMediaInstanceStatusOpening 141 | VlmMediaInstanceStatusPlaying 142 | VlmMediaInstanceStatusPause 143 | VlmMediaInstanceStatusEnd 144 | VlmMediaInstanceStatusError 145 | ) 146 | -------------------------------------------------------------------------------- /v3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/adrg/libvlc-go/v3 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /v3/list_player.go: -------------------------------------------------------------------------------- 1 | //go:build !legacy 2 | // +build !legacy 3 | 4 | package vlc 5 | 6 | // #cgo LDFLAGS: -lvlc 7 | // #include 8 | // #include 9 | import "C" 10 | 11 | // PlaybackMode defines playback modes for a media list. 12 | type PlaybackMode uint 13 | 14 | // Playback modes. 15 | const ( 16 | Default PlaybackMode = iota 17 | Loop 18 | Repeat 19 | ) 20 | 21 | // Validate checks if the playback mode valid. 22 | func (pm PlaybackMode) Validate() error { 23 | if pm > Repeat { 24 | return ErrInvalid 25 | } 26 | 27 | return nil 28 | } 29 | 30 | // ListPlayer is an enhanced media player used to play media lists. 31 | type ListPlayer struct { 32 | player *C.libvlc_media_list_player_t 33 | list *MediaList 34 | } 35 | 36 | // NewListPlayer creates a new list player instance. 37 | func NewListPlayer() (*ListPlayer, error) { 38 | if err := inst.assertInit(); err != nil { 39 | return nil, err 40 | } 41 | 42 | player := C.libvlc_media_list_player_new(inst.handle) 43 | if player == nil { 44 | return nil, errOrDefault(getError(), ErrListPlayerCreate) 45 | } 46 | 47 | return &ListPlayer{player: player}, nil 48 | } 49 | 50 | // Release destroys the list player instance. 51 | func (lp *ListPlayer) Release() error { 52 | if err := lp.assertInit(); err != nil { 53 | return nil 54 | } 55 | 56 | C.libvlc_media_list_player_release(lp.player) 57 | lp.player = nil 58 | 59 | return nil 60 | } 61 | 62 | // Player returns the underlying Player instance of the list player. 63 | func (lp *ListPlayer) Player() (*Player, error) { 64 | if err := lp.assertInit(); err != nil { 65 | return nil, err 66 | } 67 | 68 | player := C.libvlc_media_list_player_get_media_player(lp.player) 69 | if player == nil { 70 | return nil, errOrDefault(getError(), ErrPlayerNotInitialized) 71 | } 72 | 73 | // This call will not release the player. Instead, it will decrement the 74 | // reference count increased by libvlc_media_list_player_get_media_player. 75 | C.libvlc_media_player_release(player) 76 | 77 | return &Player{player: player}, nil 78 | } 79 | 80 | // SetPlayer sets the underlying Player instance of the list player. 81 | func (lp *ListPlayer) SetPlayer(player *Player) error { 82 | if err := lp.assertInit(); err != nil { 83 | return err 84 | } 85 | if err := player.assertInit(); err != nil { 86 | return err 87 | } 88 | 89 | C.libvlc_media_list_player_set_media_player(lp.player, player.player) 90 | return nil 91 | } 92 | 93 | // Play plays the current media list. 94 | func (lp *ListPlayer) Play() error { 95 | if err := lp.assertInit(); err != nil { 96 | return err 97 | } 98 | if lp.IsPlaying() { 99 | return nil 100 | } 101 | 102 | C.libvlc_media_list_player_play(lp.player) 103 | return getError() 104 | } 105 | 106 | // PlayNext plays the next media in the current media list. 107 | func (lp *ListPlayer) PlayNext() error { 108 | if err := lp.assertInit(); err != nil { 109 | return err 110 | } 111 | 112 | if C.libvlc_media_list_player_next(lp.player) < 0 { 113 | return errOrDefault(getError(), ErrPlayerPlay) 114 | } 115 | 116 | return nil 117 | } 118 | 119 | // PlayPrevious plays the previous media in the current media list. 120 | func (lp *ListPlayer) PlayPrevious() error { 121 | if err := lp.assertInit(); err != nil { 122 | return err 123 | } 124 | 125 | if C.libvlc_media_list_player_previous(lp.player) < 0 { 126 | return errOrDefault(getError(), ErrPlayerPlay) 127 | } 128 | 129 | return nil 130 | } 131 | 132 | // PlayAtIndex plays the media at the specified index from the 133 | // current media list. 134 | func (lp *ListPlayer) PlayAtIndex(index uint) error { 135 | if err := lp.assertInit(); err != nil { 136 | return err 137 | } 138 | 139 | idx := C.int(index) 140 | if C.libvlc_media_list_player_play_item_at_index(lp.player, idx) < 0 { 141 | return errOrDefault(getError(), ErrPlayerPlay) 142 | } 143 | 144 | return nil 145 | } 146 | 147 | // PlayItem plays the specified media item. The item must be part of the 148 | // current media list of the player. 149 | func (lp *ListPlayer) PlayItem(m *Media) error { 150 | if err := lp.assertInit(); err != nil { 151 | return err 152 | } 153 | if err := m.assertInit(); err != nil { 154 | return err 155 | } 156 | 157 | if C.libvlc_media_list_player_play_item(lp.player, m.media) < 0 { 158 | return errOrDefault(getError(), ErrMediaNotFound) 159 | } 160 | 161 | return nil 162 | } 163 | 164 | // IsPlaying returns a boolean value specifying if the player is currently 165 | // playing. 166 | func (lp *ListPlayer) IsPlaying() bool { 167 | if err := lp.assertInit(); err != nil { 168 | return false 169 | } 170 | 171 | return C.libvlc_media_list_player_is_playing(lp.player) != 0 172 | } 173 | 174 | // Stop cancels the currently playing media list, if there is one. 175 | func (lp *ListPlayer) Stop() error { 176 | if err := lp.assertInit(); err != nil { 177 | return err 178 | } 179 | 180 | C.libvlc_media_list_player_stop(lp.player) 181 | return getError() 182 | } 183 | 184 | // SetPause sets the pause state of the list player. 185 | // Pass in `true` to pause the current media, or `false` to resume it. 186 | func (lp *ListPlayer) SetPause(pause bool) error { 187 | if err := lp.assertInit(); err != nil { 188 | return err 189 | } 190 | 191 | C.libvlc_media_list_player_set_pause(lp.player, C.int(boolToInt(pause))) 192 | return getError() 193 | } 194 | 195 | // TogglePause pauses/resumes the player. 196 | // Calling this method has no effect if there is no media. 197 | func (lp *ListPlayer) TogglePause() error { 198 | if err := lp.assertInit(); err != nil { 199 | return err 200 | } 201 | 202 | C.libvlc_media_list_player_pause(lp.player) 203 | return getError() 204 | } 205 | 206 | // SetPlaybackMode sets the player playback mode for the media list. 207 | // By default, it plays the media list once and then stops. 208 | func (lp *ListPlayer) SetPlaybackMode(mode PlaybackMode) error { 209 | if err := lp.assertInit(); err != nil { 210 | return err 211 | } 212 | if err := mode.Validate(); err != nil { 213 | return err 214 | } 215 | 216 | C.libvlc_media_list_player_set_playback_mode( 217 | lp.player, 218 | C.libvlc_playback_mode_t(mode), 219 | ) 220 | return nil 221 | } 222 | 223 | // MediaState returns the state of the current media. 224 | func (lp *ListPlayer) MediaState() (MediaState, error) { 225 | if err := lp.assertInit(); err != nil { 226 | return MediaNothingSpecial, err 227 | } 228 | 229 | return MediaState(C.libvlc_media_list_player_get_state(lp.player)), nil 230 | } 231 | 232 | // MediaList returns the current media list of the player, if one exists. 233 | func (lp *ListPlayer) MediaList() *MediaList { 234 | return lp.list 235 | } 236 | 237 | // SetMediaList sets the media list to be played. 238 | func (lp *ListPlayer) SetMediaList(ml *MediaList) error { 239 | if err := lp.assertInit(); err != nil { 240 | return err 241 | } 242 | if err := ml.assertInit(); err != nil { 243 | return err 244 | } 245 | 246 | lp.list = ml 247 | C.libvlc_media_list_player_set_media_list(lp.player, ml.list) 248 | return nil 249 | } 250 | 251 | // EventManager returns the event manager responsible for the list player. 252 | func (lp *ListPlayer) EventManager() (*EventManager, error) { 253 | if err := lp.assertInit(); err != nil { 254 | return nil, err 255 | } 256 | 257 | manager := C.libvlc_media_list_player_event_manager(lp.player) 258 | if manager == nil { 259 | return nil, ErrMissingEventManager 260 | } 261 | 262 | return newEventManager(manager), nil 263 | } 264 | 265 | func (lp *ListPlayer) assertInit() error { 266 | if lp == nil || lp.player == nil { 267 | return ErrListPlayerNotInitialized 268 | } 269 | 270 | return nil 271 | } 272 | -------------------------------------------------------------------------------- /v3/logo.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | /* 4 | #cgo LDFLAGS: -lvlc 5 | #include 6 | #include 7 | */ 8 | import "C" 9 | import ( 10 | "fmt" 11 | "image" 12 | "image/png" 13 | "os" 14 | "strings" 15 | "time" 16 | "unsafe" 17 | ) 18 | 19 | // LogoFile represents a logo file which can be used as a media player's logo. 20 | // The logo of a player can also be composed of a series of alternating files. 21 | type LogoFile struct { 22 | path string 23 | displayDuration time.Duration 24 | opacity int 25 | } 26 | 27 | // NewLogoFileFromPath returns a new logo file with the specified path. 28 | // The file is displyed for the provided duration. If the specified display 29 | // duration is negative, the global display duration set on the logo the file 30 | // is applied to is used. 31 | // The provided opacity must be a value between 0 (transparent) and 255 (opaque). 32 | // If the specified opacity is negative, the global opacity set on the logo 33 | // the file is applied to is used. 34 | func NewLogoFileFromPath(path string, displayDuration time.Duration, opacity int) (*LogoFile, error) { 35 | if path == "" { 36 | return nil, ErrInvalid 37 | } 38 | 39 | return &LogoFile{ 40 | path: path, 41 | displayDuration: displayDuration, 42 | opacity: opacity, 43 | }, nil 44 | } 45 | 46 | // NewLogoFileFromImage returns a new logo file with the specified image. 47 | // The file is displyed for the provided duration. If the specified display 48 | // duration is negative, the global display duration set on the logo the file 49 | // is applied to is used. 50 | // The provided opacity must be a value between 0 (transparent) and 255 (opaque). 51 | // If the specified opacity is negative, the global opacity set on the logo 52 | // the file is applied to is used. 53 | func NewLogoFileFromImage(img image.Image, displayDuration time.Duration, opacity int) (*LogoFile, error) { 54 | if img == nil { 55 | return nil, ErrInvalid 56 | } 57 | 58 | // Create temporary file. 59 | f, err := os.CreateTemp("", "logo_*.png") 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | // Encode logo image to temporary file. 65 | if err := png.Encode(f, img); err != nil { 66 | return nil, err 67 | } 68 | 69 | // Close temporary file. 70 | path := f.Name() 71 | if err := f.Close(); err != nil { 72 | return nil, err 73 | } 74 | 75 | return NewLogoFileFromPath(path, displayDuration, opacity) 76 | } 77 | 78 | // Logo represents a logo that can be displayed over a media instance. 79 | // 80 | // For more information see https://wiki.videolan.org/Documentation:Modules/logo. 81 | type Logo struct { 82 | player *Player 83 | } 84 | 85 | func newLogo(player *Player) *Logo { 86 | return &Logo{ 87 | player: player, 88 | } 89 | } 90 | 91 | // Enable enables or disables the logo. By default, the logo is disabled. 92 | func (l *Logo) Enable(enable bool) error { 93 | return l.setInt(C.libvlc_logo_enable, boolToInt(enable)) 94 | } 95 | 96 | // SetFiles sets the sequence of files to be displayed for the logo. 97 | func (l *Logo) SetFiles(files ...*LogoFile) error { 98 | fileFmts := make([]string, 0, len(files)) 99 | for _, file := range files { 100 | if file == nil { 101 | continue 102 | } 103 | fileFmt := file.path 104 | if file.displayDuration >= 0 { 105 | fileFmt = fmt.Sprintf("%s,%d", fileFmt, file.displayDuration.Milliseconds()) 106 | } 107 | if file.opacity >= 0 { 108 | fileFmt = fmt.Sprintf("%s,%d", fileFmt, file.opacity) 109 | } 110 | 111 | fileFmts = append(fileFmts, fileFmt) 112 | } 113 | 114 | if len(fileFmts) == 0 { 115 | return nil 116 | } 117 | 118 | return l.setString(C.libvlc_logo_file, strings.Join(fileFmts, ";")) 119 | } 120 | 121 | // Position returns the position of the logo, relative to its container. 122 | // Default: vlc.PositionTopLeft. 123 | func (l *Logo) Position() (Position, error) { 124 | iVal, err := l.getInt(C.libvlc_logo_position) 125 | if err != nil { 126 | return PositionDisable, err 127 | } 128 | 129 | switch { 130 | case iVal >= 8: 131 | iVal -= 2 132 | case iVal >= 4: 133 | iVal-- 134 | } 135 | 136 | if iVal < 0 { 137 | return PositionTopLeft, nil 138 | } 139 | return Position(iVal), nil 140 | } 141 | 142 | // SetPosition sets the position of the logo, relative to its container. 143 | func (l *Logo) SetPosition(position Position) error { 144 | switch { 145 | case position >= PositionBottom: 146 | position += 2 147 | case position >= PositionTop: 148 | position++ 149 | } 150 | 151 | return l.setInt(C.libvlc_logo_position, int(position)) 152 | } 153 | 154 | // X returns the X coordinate of the logo. The returned value is 155 | // relative to the position of the logo inside its container, i.e. the 156 | // position set using Logo.SetPosition method. 157 | // Default: 0. 158 | func (l *Logo) X() (int, error) { 159 | return l.getInt(C.libvlc_logo_x) 160 | } 161 | 162 | // SetX sets the X coordinate of the logo. The value is specified 163 | // relative to the position of the logo inside its container, i.e. the 164 | // position set using the `Logo.SetPosition` method. 165 | // 166 | // NOTE: the method has no effect if the position of the logo is set to 167 | // `vlc.PositionCenter`, `vlc.PositionTop` or `vlc.PositionBottom`. 168 | func (l *Logo) SetX(x int) error { 169 | return l.setInt(C.libvlc_logo_x, x) 170 | } 171 | 172 | // Y returns the Y coordinate of the logo. The returned value is 173 | // relative to the position of the logo inside its container, i.e. the 174 | // position set using the `Logo.SetPosition` method. 175 | // Default: 0. 176 | func (l *Logo) Y() (int, error) { 177 | return l.getInt(C.libvlc_logo_y) 178 | } 179 | 180 | // SetY sets the Y coordinate of the logo. The value is specified 181 | // relative to the position of the logo inside its container. 182 | // 183 | // NOTE: the method has no effect if the position of the logo is set to 184 | // `vlc.PositionCenter`, `vlc.PositionLeft` or `vlc.PositionRight`. 185 | func (l *Logo) SetY(y int) error { 186 | return l.setInt(C.libvlc_logo_y, y) 187 | } 188 | 189 | // Opacity returns the global opacity of the logo. 190 | // The returned opacity is a value between 0 (transparent) and 255 (opaque). 191 | // The global opacity can be overridden by each provided logo file. 192 | // Default: 255. 193 | func (l *Logo) Opacity() (int, error) { 194 | return l.getInt(C.libvlc_logo_opacity) 195 | } 196 | 197 | // SetOpacity sets the global opacity of the logo. If an opacity override is 198 | // not specified when setting the logo files, the global opacity is used. The 199 | // opacity is specified as an integer between 0 (transparent) and 255 (opaque). 200 | // The global opacity can be overridden by each provided logo file. 201 | func (l *Logo) SetOpacity(opacity int) error { 202 | return l.setInt(C.libvlc_logo_opacity, opacity) 203 | } 204 | 205 | // DisplayDuration returns the global duration for which a logo file 206 | // is set to be displayed before displaying the next one (if one is available). 207 | // The global display duration can be overridden by each provided logo file. 208 | // Default: 1s. 209 | func (l *Logo) DisplayDuration() (time.Duration, error) { 210 | iVal, err := l.getInt(C.libvlc_logo_delay) 211 | return time.Duration(iVal) * time.Millisecond, err 212 | } 213 | 214 | // SetDisplayDuration sets the duration for which to display a logo file 215 | // before displaying the next one (if one is available). 216 | // The global display duration can be overridden by each provided logo file. 217 | func (l *Logo) SetDisplayDuration(displayDuration time.Duration) error { 218 | return l.setInt(C.libvlc_logo_delay, int(displayDuration.Milliseconds())) 219 | } 220 | 221 | // RepeatCount returns the number of times the logo sequence is set 222 | // to be repeated. 223 | func (l *Logo) RepeatCount() (int, error) { 224 | return l.getInt(C.libvlc_logo_repeat) 225 | } 226 | 227 | // SetRepeatCount sets the number of times the logo sequence should repeat. 228 | // Pass in `-1` to repeat the logo sequence indefinitely, `0` to disable logo 229 | // sequence looping or a positive number to repeat the logo sequence a specific 230 | // number of times. 231 | // Default: -1 (the logo sequence is repeated indefinitely). 232 | func (l *Logo) SetRepeatCount(count int) error { 233 | if count > 0 { 234 | count++ 235 | } 236 | 237 | return l.setInt(C.libvlc_logo_repeat, count) 238 | } 239 | 240 | func (l *Logo) getInt(option C.uint) (int, error) { 241 | if err := l.player.assertInit(); err != nil { 242 | return 0, err 243 | } 244 | 245 | return int(C.libvlc_video_get_logo_int(l.player.player, option)), nil 246 | } 247 | 248 | func (l *Logo) setInt(option C.uint, val int) error { 249 | if err := l.player.assertInit(); err != nil { 250 | return err 251 | } 252 | 253 | C.libvlc_video_set_logo_int(l.player.player, option, C.int(val)) 254 | return nil 255 | } 256 | 257 | func (l *Logo) setString(option C.uint, val string) error { 258 | if err := l.player.assertInit(); err != nil { 259 | return err 260 | } 261 | 262 | cVal := C.CString(val) 263 | defer C.free(unsafe.Pointer(cVal)) 264 | 265 | C.libvlc_video_set_logo_string(l.player.player, option, cVal) 266 | return nil 267 | } 268 | -------------------------------------------------------------------------------- /v3/marquee.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | /* 4 | #cgo LDFLAGS: -lvlc 5 | #include 6 | #include 7 | */ 8 | import "C" 9 | import ( 10 | "image/color" 11 | "time" 12 | "unsafe" 13 | ) 14 | 15 | // Marquee represents a marquee text than can be displayed over a media 16 | // instance, along with its visual properties. 17 | // 18 | // For more information see https://wiki.videolan.org/Documentation:Modules/marq. 19 | type Marquee struct { 20 | player *Player 21 | } 22 | 23 | func newMarquee(player *Player) *Marquee { 24 | return &Marquee{ 25 | player: player, 26 | } 27 | } 28 | 29 | // Enable enables or disables the marquee. By default, the marquee is disabled. 30 | func (m *Marquee) Enable(enable bool) error { 31 | return m.setInt(C.libvlc_marquee_Enable, boolToInt(enable)) 32 | } 33 | 34 | // Text returns the marquee text. 35 | // Default: "". 36 | func (m *Marquee) Text() (string, error) { 37 | return m.getString(C.libvlc_marquee_Text) 38 | } 39 | 40 | // SetText sets the marquee text. 41 | // The specified text can contain time format string sequences which are 42 | // converted to the requested time values at runtime. Most of the time 43 | // conversion specifiers supported by the `strftime` C function can be used. 44 | // 45 | // Common time format string sequences: 46 | // %Y = year, %m = month, %d = day, %H = hour, %M = minute, %S = second. 47 | // For more information see https://en.cppreference.com/w/c/chrono/strftime. 48 | func (m *Marquee) SetText(text string) error { 49 | return m.setString(C.libvlc_marquee_Text, text) 50 | } 51 | 52 | // Color returns the marquee text color. 53 | // Opacity information is included in the returned color. 54 | // Default: white. 55 | func (m *Marquee) Color() (color.Color, error) { 56 | // Get color. 57 | rgb, err := m.getInt(C.libvlc_marquee_Color) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | // Get alpha. 63 | alpha, err := m.getInt(C.libvlc_marquee_Opacity) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | return color.RGBA{ 69 | R: uint8((rgb >> 16) & 0x0ff), 70 | G: uint8((rgb >> 8) & 0x0ff), 71 | B: uint8((rgb) & 0x0ff), 72 | A: uint8(alpha & 0x0ff), 73 | }, err 74 | } 75 | 76 | // SetColor sets the color of the marquee text. The opacity of the text 77 | // is also set, based on the alpha value of the color. 78 | func (m *Marquee) SetColor(color color.Color) error { 79 | r, g, b, a := color.RGBA() 80 | 81 | // Set color. 82 | rgb := int((((r >> 8) & 0x0ff) << 16) | 83 | (((g >> 8) & 0x0ff) << 8) | 84 | ((b >> 8) & 0x0ff)) 85 | if err := m.setInt(C.libvlc_marquee_Color, rgb); err != nil { 86 | return err 87 | } 88 | 89 | // Set alpha. 90 | alpha := int((a >> 8) & 0x0ff) 91 | if err := m.setInt(C.libvlc_marquee_Opacity, alpha); err != nil { 92 | return err 93 | } 94 | 95 | return nil 96 | } 97 | 98 | // Opacity returns the opacity of the marquee text. 99 | // The returned opacity is a value between 0 (transparent) and 255 (opaque). 100 | // Default: 255. 101 | func (m *Marquee) Opacity() (int, error) { 102 | return m.getInt(C.libvlc_marquee_Opacity) 103 | } 104 | 105 | // SetOpacity sets the opacity of the marquee text. The opacity is specified 106 | // as an integer between 0 (transparent) and 255 (opaque). 107 | func (m *Marquee) SetOpacity(opacity int) error { 108 | return m.setInt(C.libvlc_marquee_Opacity, opacity) 109 | } 110 | 111 | // Position returns the position of the marquee, relative to its container. 112 | // Default: vlc.PositionTopLeft. 113 | func (m *Marquee) Position() (Position, error) { 114 | iVal, err := m.getInt(C.libvlc_marquee_Position) 115 | if err != nil { 116 | return PositionDisable, err 117 | } 118 | 119 | switch { 120 | case iVal >= 8: 121 | iVal -= 2 122 | case iVal >= 4: 123 | iVal-- 124 | } 125 | 126 | if iVal < 0 { 127 | return PositionTopLeft, nil 128 | } 129 | return Position(iVal), nil 130 | } 131 | 132 | // SetPosition sets the position of the marquee, relative to its container. 133 | func (m *Marquee) SetPosition(position Position) error { 134 | switch { 135 | case position >= PositionBottom: 136 | position += 2 137 | case position >= PositionTop: 138 | position++ 139 | } 140 | 141 | return m.setInt(C.libvlc_marquee_Position, int(position)) 142 | } 143 | 144 | // X returns the X coordinate of the marquee text. The returned value is 145 | // relative to the position of the marquee inside its container, i.e. the 146 | // position set using the `Marquee.SetPosition` method. 147 | // Default: 0. 148 | func (m *Marquee) X() (int, error) { 149 | return m.getInt(C.libvlc_marquee_X) 150 | } 151 | 152 | // SetX sets the X coordinate of the marquee text. The value is specified 153 | // relative to the position of the marquee inside its container. 154 | // 155 | // NOTE: the method has no effect if the position of the marquee is set to 156 | // `vlc.PositionCenter`, `vlc.PositionTop` or `vlc.PositionBottom`. 157 | func (m *Marquee) SetX(x int) error { 158 | return m.setInt(C.libvlc_marquee_X, x) 159 | } 160 | 161 | // Y returns the Y coordinate of the marquee text. The returned value is 162 | // relative to the position of the marquee inside its container, i.e. the 163 | // position set using the `Marquee.SetPosition` method. 164 | // Default: 0. 165 | func (m *Marquee) Y() (int, error) { 166 | return m.getInt(C.libvlc_marquee_Y) 167 | } 168 | 169 | // SetY sets the Y coordinate of the marquee text. The value is specified 170 | // relative to the position of the marquee inside its container. 171 | // 172 | // NOTE: the method has no effect if the position of the marquee is set to 173 | // `vlc.PositionCenter`, `vlc.PositionLeft` or `vlc.PositionRight`. 174 | func (m *Marquee) SetY(y int) error { 175 | return m.setInt(C.libvlc_marquee_Y, y) 176 | } 177 | 178 | // Size returns the font size used to render the marquee text. 179 | // Default: 0 (default font size is used). 180 | func (m *Marquee) Size() (int, error) { 181 | return m.getInt(C.libvlc_marquee_Size) 182 | } 183 | 184 | // SetSize sets the font size used to render the marquee text. 185 | func (m *Marquee) SetSize(size int) error { 186 | return m.setInt(C.libvlc_marquee_Size, size) 187 | } 188 | 189 | // RefreshInterval returns the interval between marquee text updates. 190 | // The marquee text refreshes mainly when using time format string sequences. 191 | // Default: 1s. 192 | func (m *Marquee) RefreshInterval() (time.Duration, error) { 193 | iVal, err := m.getInt(C.libvlc_marquee_Refresh) 194 | return time.Duration(iVal) * time.Millisecond, err 195 | } 196 | 197 | // SetRefreshInterval sets the interval between marquee text updates. 198 | // The marquee text refreshes mainly when using time format string sequences. 199 | func (m *Marquee) SetRefreshInterval(refreshInterval time.Duration) error { 200 | return m.setInt(C.libvlc_marquee_Refresh, int(refreshInterval.Milliseconds())) 201 | } 202 | 203 | // DisplayDuration returns the duration for which the marquee text 204 | // is set to be displayed. 205 | // Default: 0 (the marquee is displayed indefinitely). 206 | func (m *Marquee) DisplayDuration() (time.Duration, error) { 207 | iVal, err := m.getInt(C.libvlc_marquee_Timeout) 208 | return time.Duration(iVal) * time.Millisecond, err 209 | } 210 | 211 | // SetDisplayDuration sets the duration for which to display the marquee text. 212 | func (m *Marquee) SetDisplayDuration(displayDuration time.Duration) error { 213 | return m.setInt(C.libvlc_marquee_Timeout, int(displayDuration.Milliseconds())) 214 | } 215 | 216 | func (m *Marquee) getInt(option C.uint) (int, error) { 217 | if err := m.player.assertInit(); err != nil { 218 | return 0, err 219 | } 220 | 221 | return int(C.libvlc_video_get_marquee_int(m.player.player, option)), nil 222 | } 223 | 224 | func (m *Marquee) setInt(option C.uint, val int) error { 225 | if err := m.player.assertInit(); err != nil { 226 | return err 227 | } 228 | 229 | C.libvlc_video_set_marquee_int(m.player.player, option, C.int(val)) 230 | return nil 231 | } 232 | 233 | func (m *Marquee) getString(option C.uint) (string, error) { 234 | if err := m.player.assertInit(); err != nil { 235 | return "", err 236 | } 237 | 238 | cVal := C.libvlc_video_get_marquee_string(m.player.player, option) 239 | if cVal == nil { 240 | return "", errOrDefault(getError(), ErrInvalid) 241 | } 242 | defer C.free(unsafe.Pointer(cVal)) 243 | 244 | return C.GoString(cVal), nil 245 | } 246 | 247 | func (m *Marquee) setString(option C.uint, val string) error { 248 | if err := m.player.assertInit(); err != nil { 249 | return err 250 | } 251 | 252 | cVal := C.CString(val) 253 | defer C.free(unsafe.Pointer(cVal)) 254 | 255 | C.libvlc_video_set_marquee_string(m.player.player, option, cVal) 256 | return nil 257 | } 258 | -------------------------------------------------------------------------------- /v3/media_discoverer.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | // #include 6 | import "C" 7 | import ( 8 | "unsafe" 9 | ) 10 | 11 | // MediaDiscoveryCallback is used by media discovery services to report 12 | // discovery events. The callback provides the event, the media instance, 13 | // and the index at which the action takes place in the media list of the 14 | // discovery service. 15 | // 16 | // The available events are: 17 | // - MediaListWillAddItem 18 | // - MediaListItemAdded 19 | // - MediaListWillDeleteItem 20 | // - MediaListItemDeleted 21 | type MediaDiscoveryCallback func(Event, *Media, int) 22 | 23 | // MediaDiscoveryCategory defines categories of media discovery services. 24 | type MediaDiscoveryCategory uint 25 | 26 | // Media discovery categories. 27 | const ( 28 | // Devices (e.g. portable devices supporting MTP, discs). 29 | MediaDiscoveryDevices MediaDiscoveryCategory = iota 30 | 31 | // LAN/WAN services (e.g. UPnP, SMB, SAP). 32 | MediaDiscoveryLAN 33 | 34 | // Internet services (e.g. podcasts, radio stations). 35 | MediaDiscoveryInternet 36 | 37 | // Local directories. 38 | MediaDiscoveryLocal 39 | ) 40 | 41 | // MediaDiscovererDescriptor contains information about a media 42 | // discovery service. Pass the `Name` field to the NewMediaDiscoverer 43 | // method in order to create a new discovery service instance. 44 | type MediaDiscovererDescriptor struct { 45 | Name string 46 | LongName string 47 | Category MediaDiscoveryCategory 48 | } 49 | 50 | // ListMediaDiscoverers returns a list of descriptors identifying the 51 | // available media discovery services of the specified category. 52 | func ListMediaDiscoverers(category MediaDiscoveryCategory) ([]*MediaDiscovererDescriptor, error) { 53 | if err := inst.assertInit(); err != nil { 54 | return nil, err 55 | } 56 | 57 | // Get media discoverer descriptors. 58 | var cDescriptors **C.libvlc_media_discoverer_description_t 59 | 60 | count := int(C.libvlc_media_discoverer_list_get(inst.handle, C.libvlc_media_discoverer_category_t(category), &cDescriptors)) 61 | if count <= 0 || cDescriptors == nil { 62 | return nil, nil 63 | } 64 | defer C.libvlc_media_discoverer_list_release(cDescriptors, C.size_t(count)) 65 | 66 | // Parse media discoverer descriptors. 67 | descriptors := make([]*MediaDiscovererDescriptor, 0, count) 68 | for i := 0; i < count; i++ { 69 | // Get current media discoverer descriptor. 70 | cDescriptorPtr := unsafe.Pointer(uintptr(unsafe.Pointer(cDescriptors)) + 71 | uintptr(i)*unsafe.Sizeof(*cDescriptors)) 72 | if cDescriptorPtr == nil { 73 | return nil, ErrMediaDiscovererParse 74 | } 75 | 76 | cDescriptor := *(**C.libvlc_media_discoverer_description_t)(cDescriptorPtr) 77 | if cDescriptor == nil { 78 | return nil, ErrMediaDiscovererParse 79 | } 80 | 81 | // Parse media discoverer descriptor. 82 | descriptors = append(descriptors, &MediaDiscovererDescriptor{ 83 | Name: C.GoString(cDescriptor.psz_name), 84 | LongName: C.GoString(cDescriptor.psz_longname), 85 | Category: MediaDiscoveryCategory(cDescriptor.i_cat), 86 | }) 87 | } 88 | 89 | return descriptors, nil 90 | } 91 | 92 | // MediaDiscoverer represents a media discovery service. 93 | // Discovery services use different discovery protocols (e.g. MTP, UPnP, SMB) 94 | // in order to find available media instances. 95 | type MediaDiscoverer struct { 96 | discoverer *C.libvlc_media_discoverer_t 97 | stopFunc func() 98 | } 99 | 100 | // NewMediaDiscoverer instantiates the media discovery service identified 101 | // by the specified name. Use the ListMediaDiscoverers method to obtain the 102 | // list of available discovery service descriptors. 103 | // 104 | // NOTE: Call the Release method on the discovery service instance in 105 | // order to free the allocated resources. 106 | func NewMediaDiscoverer(name string) (*MediaDiscoverer, error) { 107 | if err := inst.assertInit(); err != nil { 108 | return nil, err 109 | } 110 | 111 | cName := C.CString(name) 112 | defer C.free(unsafe.Pointer(cName)) 113 | 114 | discoverer := C.libvlc_media_discoverer_new(inst.handle, cName) 115 | if discoverer == nil { 116 | return nil, errOrDefault(getError(), ErrMediaDiscovererCreate) 117 | } 118 | 119 | return &MediaDiscoverer{ 120 | discoverer: discoverer, 121 | }, nil 122 | } 123 | 124 | // Release stops and destroys the media discovery service along 125 | // with all the media found by the instance. 126 | func (md *MediaDiscoverer) Release() error { 127 | if err := md.assertInit(); err != nil { 128 | return nil 129 | } 130 | 131 | // Stop discovery service. 132 | md.stop() 133 | 134 | // Release discovery service. 135 | C.libvlc_media_discoverer_release(md.discoverer) 136 | md.discoverer = nil 137 | 138 | return nil 139 | } 140 | 141 | // Start starts the media discovery service and reports discovery 142 | // events through the specified callback function. 143 | // 144 | // NOTE: The Stop and Release methods should not be called from the callback 145 | // function. Doing so will result in undefined behavior. 146 | func (md *MediaDiscoverer) Start(cb MediaDiscoveryCallback) error { 147 | if cb == nil { 148 | return ErrInvalidEventCallback 149 | } 150 | 151 | // Stop discovery service, if started. 152 | if err := md.Stop(); err != nil { 153 | return err 154 | } 155 | 156 | // Retrieve media list. 157 | ml, err := md.MediaList() 158 | if err != nil { 159 | return err 160 | } 161 | 162 | // Retrieve media list event manager. 163 | manager, err := ml.EventManager() 164 | if err != nil { 165 | return err 166 | } 167 | 168 | // Create event callback. 169 | eventCallback := func(event *C.libvlc_event_t, userData interface{}) { 170 | if err := md.assertInit(); err != nil { 171 | return 172 | } 173 | if event == nil { 174 | return 175 | } 176 | 177 | // Parse event media. 178 | cMedia := *(**C.libvlc_media_t)(unsafe.Pointer(&event.u[0])) 179 | if cMedia == nil { 180 | return 181 | } 182 | media := &Media{media: cMedia} 183 | 184 | // Parse event media index. 185 | cIndex := (*C.int)(unsafe.Pointer(uintptr(unsafe.Pointer(&event.u[0])) + 186 | unsafe.Sizeof(cMedia))) 187 | if cIndex == nil { 188 | return 189 | } 190 | index := int(*cIndex) 191 | 192 | switch event := Event(event._type); event { 193 | case MediaListWillAddItem, MediaListItemAdded, 194 | MediaListWillDeleteItem, MediaListItemDeleted: 195 | cb(event, media, index) 196 | } 197 | } 198 | 199 | // Attach discovery service events. 200 | events := []Event{ 201 | MediaListWillAddItem, 202 | MediaListItemAdded, 203 | MediaListWillDeleteItem, 204 | MediaListItemDeleted, 205 | } 206 | 207 | eventIDs := make([]EventID, 0, len(events)) 208 | for _, event := range events { 209 | eventID, err := manager.attach(event, nil, eventCallback, nil) 210 | if err != nil { 211 | return err 212 | } 213 | 214 | eventIDs = append(eventIDs, eventID) 215 | } 216 | defer func() { 217 | if md.stopFunc == nil { 218 | manager.Detach(eventIDs...) 219 | } 220 | }() 221 | 222 | // Start discovery service. 223 | if C.libvlc_media_discoverer_start(md.discoverer) < 0 { 224 | return errOrDefault(getError(), ErrMediaDiscovererStart) 225 | } 226 | 227 | md.stopFunc = func() { 228 | // Detach events. 229 | manager.Detach(eventIDs...) 230 | 231 | // Stop discovery service. 232 | C.libvlc_media_discoverer_stop(md.discoverer) 233 | } 234 | 235 | return nil 236 | } 237 | 238 | // Stop stops the discovery service. 239 | func (md *MediaDiscoverer) Stop() error { 240 | if err := md.assertInit(); err != nil { 241 | return err 242 | } 243 | 244 | md.stop() 245 | return nil 246 | } 247 | 248 | // IsRunning returns true if the media discovery service is running. 249 | func (md *MediaDiscoverer) IsRunning() bool { 250 | if err := md.assertInit(); err != nil { 251 | return false 252 | } 253 | 254 | return C.libvlc_media_discoverer_is_running(md.discoverer) != 0 255 | } 256 | 257 | // MediaList returns the media list associated with the discovery service, 258 | // which contains the found media instances. 259 | // 260 | // NOTE: The returned media list is read-only. 261 | func (md *MediaDiscoverer) MediaList() (*MediaList, error) { 262 | if err := md.assertInit(); err != nil { 263 | return nil, err 264 | } 265 | 266 | ml := C.libvlc_media_discoverer_media_list(md.discoverer) 267 | if ml == nil { 268 | return nil, errOrDefault(getError(), ErrMediaListNotFound) 269 | } 270 | 271 | // This call will not release the media list. Instead, it will decrement 272 | // the reference count increased by libvlc_media_discoverer_media_list. 273 | C.libvlc_media_list_release(ml) 274 | 275 | return &MediaList{list: ml}, nil 276 | } 277 | 278 | func (md *MediaDiscoverer) stop() { 279 | if md.stopFunc != nil { 280 | md.stopFunc() 281 | md.stopFunc = nil 282 | } 283 | } 284 | 285 | func (md *MediaDiscoverer) assertInit() error { 286 | if md == nil || md.discoverer == nil { 287 | return ErrMediaDiscovererNotInitialized 288 | } 289 | 290 | return nil 291 | } 292 | -------------------------------------------------------------------------------- /v3/media_list.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | import "C" 6 | import "io" 7 | 8 | // MediaList represents a collection of media files. 9 | type MediaList struct { 10 | list *C.libvlc_media_list_t 11 | } 12 | 13 | // NewMediaList creates an empty media list. 14 | func NewMediaList() (*MediaList, error) { 15 | if err := inst.assertInit(); err != nil { 16 | return nil, err 17 | } 18 | 19 | var list *C.libvlc_media_list_t 20 | if list = C.libvlc_media_list_new(inst.handle); list == nil { 21 | return nil, errOrDefault(getError(), ErrMediaListCreate) 22 | } 23 | 24 | return &MediaList{list: list}, nil 25 | } 26 | 27 | // Release destroys the media list instance. 28 | func (ml *MediaList) Release() error { 29 | if err := ml.assertInit(); err != nil { 30 | return nil 31 | } 32 | 33 | C.libvlc_media_list_release(ml.list) 34 | ml.list = nil 35 | 36 | return nil 37 | } 38 | 39 | // AddMedia adds the provided Media instance at the end of the media list. 40 | func (ml *MediaList) AddMedia(m *Media) error { 41 | if err := m.assertInit(); err != nil { 42 | return err 43 | } 44 | 45 | // Check if media list is read-only. 46 | isReadOnly, err := ml.IsReadOnly() 47 | if err != nil { 48 | return err 49 | } 50 | if isReadOnly { 51 | return ErrMediaListReadOnly 52 | } 53 | 54 | // Lock media list. 55 | if err := ml.Lock(); err != nil { 56 | return err 57 | } 58 | defer ml.unlock() 59 | 60 | // Add the media to the list. 61 | if C.libvlc_media_list_add_media(ml.list, m.media) < 0 { 62 | return errOrDefault(getError(), ErrMediaListActionFailed) 63 | } 64 | 65 | return nil 66 | } 67 | 68 | // AddMediaFromPath loads the media file at the specified path and adds it at 69 | // the end of the media list. 70 | func (ml *MediaList) AddMediaFromPath(path string) error { 71 | media, err := NewMediaFromPath(path) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | // Add the media to the list. 77 | if err := ml.AddMedia(media); err != nil { 78 | media.release() 79 | return err 80 | } 81 | 82 | return nil 83 | } 84 | 85 | // AddMediaFromURL loads the media file at the specified URL and adds it at 86 | // the end of the the media list. 87 | func (ml *MediaList) AddMediaFromURL(url string) error { 88 | media, err := NewMediaFromURL(url) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | if err := ml.AddMedia(media); err != nil { 94 | media.release() 95 | return err 96 | } 97 | 98 | return nil 99 | } 100 | 101 | // AddMediaFromReadSeeker loads the media from the provided read 102 | // seeker and adds it at the end of the media list. 103 | func (ml *MediaList) AddMediaFromReadSeeker(r io.ReadSeeker) error { 104 | media, err := NewMediaFromReadSeeker(r) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | if err := ml.AddMedia(media); err != nil { 110 | media.release() 111 | return err 112 | } 113 | 114 | return nil 115 | } 116 | 117 | // InsertMedia inserts the provided Media instance in the list, 118 | // at the specified index. 119 | func (ml *MediaList) InsertMedia(m *Media, index uint) error { 120 | if err := m.assertInit(); err != nil { 121 | return err 122 | } 123 | 124 | // Check if media list is read-only. 125 | isReadOnly, err := ml.IsReadOnly() 126 | if err != nil { 127 | return err 128 | } 129 | if isReadOnly { 130 | return ErrMediaListReadOnly 131 | } 132 | 133 | // Lock media list. 134 | if err := ml.Lock(); err != nil { 135 | return err 136 | } 137 | defer ml.unlock() 138 | 139 | // Insert the media in the list. 140 | if C.libvlc_media_list_insert_media(ml.list, m.media, C.int(index)) < 0 { 141 | return errOrDefault(getError(), ErrMediaListActionFailed) 142 | } 143 | 144 | return nil 145 | } 146 | 147 | // InsertMediaFromPath loads the media file at the provided path and inserts 148 | // it in the list, at the specified index. 149 | func (ml *MediaList) InsertMediaFromPath(path string, index uint) error { 150 | media, err := NewMediaFromPath(path) 151 | if err != nil { 152 | return err 153 | } 154 | 155 | // Insert the media in the list. 156 | if err := ml.InsertMedia(media, index); err != nil { 157 | media.release() 158 | return err 159 | } 160 | 161 | return nil 162 | } 163 | 164 | // InsertMediaFromURL loads the media file at the provided URL and inserts 165 | // it in the list, at the specified index. 166 | func (ml *MediaList) InsertMediaFromURL(url string, index uint) error { 167 | media, err := NewMediaFromURL(url) 168 | if err != nil { 169 | return err 170 | } 171 | 172 | // Insert the media in the list. 173 | if err := ml.InsertMedia(media, index); err != nil { 174 | media.release() 175 | return err 176 | } 177 | 178 | return nil 179 | } 180 | 181 | // InsertMediaFromReadSeeker loads the media from the provided read 182 | // seeker and inserts it in the list, at the specified index. 183 | func (ml *MediaList) InsertMediaFromReadSeeker(r io.ReadSeeker, index uint) error { 184 | media, err := NewMediaFromReadSeeker(r) 185 | if err != nil { 186 | return err 187 | } 188 | 189 | // Insert the media in the list. 190 | if err := ml.InsertMedia(media, index); err != nil { 191 | media.release() 192 | return err 193 | } 194 | 195 | return nil 196 | } 197 | 198 | // RemoveMediaAtIndex removes the media item at the specified index 199 | // from the list. 200 | func (ml *MediaList) RemoveMediaAtIndex(index uint) error { 201 | // Check if media list is read-only. 202 | isReadOnly, err := ml.IsReadOnly() 203 | if err != nil { 204 | return err 205 | } 206 | if isReadOnly { 207 | return ErrMediaListReadOnly 208 | } 209 | 210 | // Lock media list. 211 | if err := ml.Lock(); err != nil { 212 | return err 213 | } 214 | defer ml.unlock() 215 | 216 | // Remove the media from the list. 217 | if C.libvlc_media_list_remove_index(ml.list, C.int(index)) < 0 { 218 | return errOrDefault(getError(), ErrMediaListActionFailed) 219 | } 220 | 221 | return nil 222 | } 223 | 224 | // MediaAtIndex returns the media item at the specified index from the list. 225 | func (ml *MediaList) MediaAtIndex(index uint) (*Media, error) { 226 | // Lock media list. 227 | if err := ml.Lock(); err != nil { 228 | return nil, err 229 | } 230 | defer ml.unlock() 231 | 232 | // Retrieve the media at the specified index. 233 | media := C.libvlc_media_list_item_at_index(ml.list, C.int(index)) 234 | if media == nil { 235 | return nil, errOrDefault(getError(), ErrMediaListActionFailed) 236 | } 237 | 238 | // This call will not release the media. Instead, it will decrement 239 | // the reference count increased by libvlc_media_list_item_at_index. 240 | C.libvlc_media_release(media) 241 | 242 | return &Media{media}, nil 243 | } 244 | 245 | // IndexOfMedia returns the index of the specified media item in the list. 246 | // 247 | // NOTE: The same instance of a media item can be present multiple times 248 | // in the list. The method returns the first matched index. 249 | func (ml *MediaList) IndexOfMedia(m *Media) (int, error) { 250 | if err := m.assertInit(); err != nil { 251 | return 0, err 252 | } 253 | 254 | if err := ml.Lock(); err != nil { 255 | return 0, err 256 | } 257 | defer ml.unlock() 258 | 259 | // Retrieve the index of the media. 260 | idx := int(C.libvlc_media_list_index_of_item(ml.list, m.media)) 261 | if idx < 0 { 262 | return 0, errOrDefault(getError(), ErrMediaNotFound) 263 | } 264 | 265 | return idx, nil 266 | } 267 | 268 | // Count returns the number of media items in the list. 269 | func (ml *MediaList) Count() (int, error) { 270 | // Lock media list. 271 | if err := ml.Lock(); err != nil { 272 | return 0, err 273 | } 274 | defer ml.unlock() 275 | 276 | // Retrieve media count. 277 | return int(C.libvlc_media_list_count(ml.list)), nil 278 | } 279 | 280 | // IsReadOnly specifies if the media list can be modified. 281 | func (ml *MediaList) IsReadOnly() (bool, error) { 282 | if err := ml.assertInit(); err != nil { 283 | return false, err 284 | } 285 | 286 | return C.libvlc_media_list_is_readonly(ml.list) != C.int(0), nil 287 | } 288 | 289 | // AssociatedMedia returns the media instance associated with the list, 290 | // if one exists. A media instance is automatically associated with the 291 | // list of its sub-items. 292 | // 293 | // NOTE: Do not call Release on the returned media instance. 294 | func (ml *MediaList) AssociatedMedia() (*Media, error) { 295 | if err := ml.assertInit(); err != nil { 296 | return nil, err 297 | } 298 | 299 | media := C.libvlc_media_list_media(ml.list) 300 | if media == nil { 301 | return nil, errOrDefault(getError(), ErrMediaNotFound) 302 | } 303 | 304 | // This call will not release the media. Instead, it will decrement 305 | // the reference count increased by libvlc_media_list_media. 306 | C.libvlc_media_release(media) 307 | 308 | return &Media{media: media}, nil 309 | } 310 | 311 | // AssociateMedia associates the specified media with the media list instance. 312 | // 313 | // NOTE: If another media instance is already associated with the list, 314 | // it will be released. 315 | func (ml *MediaList) AssociateMedia(m *Media) error { 316 | if err := ml.assertInit(); err != nil { 317 | return err 318 | } 319 | if err := m.assertInit(); err != nil { 320 | return err 321 | } 322 | 323 | C.libvlc_media_list_set_media(ml.list, m.media) 324 | return nil 325 | } 326 | 327 | // Lock makes the caller the current owner of the media list. 328 | func (ml *MediaList) Lock() error { 329 | if err := ml.assertInit(); err != nil { 330 | return err 331 | } 332 | 333 | C.libvlc_media_list_lock(ml.list) 334 | return nil 335 | } 336 | 337 | // Unlock releases ownership of the media list. 338 | func (ml *MediaList) Unlock() error { 339 | if err := ml.assertInit(); err != nil { 340 | return err 341 | } 342 | 343 | ml.unlock() 344 | return nil 345 | } 346 | 347 | // EventManager returns the event manager responsible for the media list. 348 | func (ml *MediaList) EventManager() (*EventManager, error) { 349 | if err := ml.assertInit(); err != nil { 350 | return nil, err 351 | } 352 | 353 | manager := C.libvlc_media_list_event_manager(ml.list) 354 | if manager == nil { 355 | return nil, ErrMissingEventManager 356 | } 357 | 358 | return newEventManager(manager), nil 359 | } 360 | 361 | func (ml *MediaList) assertInit() error { 362 | if ml == nil || ml.list == nil { 363 | return ErrMediaListNotInitialized 364 | } 365 | 366 | return nil 367 | } 368 | 369 | func (ml *MediaList) unlock() { 370 | C.libvlc_media_list_unlock(ml.list) 371 | } 372 | -------------------------------------------------------------------------------- /v3/media_track.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | import "C" 6 | import ( 7 | "unsafe" 8 | ) 9 | 10 | // VideoOrientation represents the orientation of a video media track. 11 | type VideoOrientation int 12 | 13 | // Video orientations. 14 | const ( 15 | // Normal. 16 | OrientationTopLeft VideoOrientation = iota 17 | 18 | // Flipped horizontally. 19 | OrientationTopRight 20 | 21 | // Flipped vertically. 22 | OrientationBottomLeft 23 | 24 | // Rotated 180 degrees. 25 | OrientationBottomRight 26 | 27 | // Transposed. 28 | OrientationLeftTop 29 | 30 | // Rotated 90 degrees anti-clockwise. 31 | OrientationLeftBottom 32 | 33 | // Rotated 90 degrees clockwise. 34 | OrientationRightTop 35 | 36 | // Anti-transposed. 37 | OrientationRightBottom 38 | ) 39 | 40 | // VideoProjection represents the projection mode of a video media track. 41 | type VideoProjection int 42 | 43 | // Video projections. 44 | const ( 45 | ProjectionRectangular VideoProjection = 0 46 | ProjectionEquirectangular VideoProjection = 1 47 | ProjectionCubemapLayoutStandard VideoProjection = 0x100 48 | ) 49 | 50 | // VideoViewpoint contains viewpoint information for a video media track. 51 | type VideoViewpoint struct { 52 | Yaw float64 // Viewpoint yaw in degrees [-180-180]. 53 | Pitch float64 // Viewpoint pitch in degrees [-90-90]. 54 | Roll float64 // Viewpoint roll in degrees [-180-180]. 55 | FOV float64 // Viewpoint field of view in degrees [0-180]. Default: 80. 56 | } 57 | 58 | // MediaTrackType represents the type of a media track. 59 | type MediaTrackType int 60 | 61 | // Media track types. 62 | const ( 63 | MediaTrackUnknown MediaTrackType = iota - 1 64 | MediaTrackAudio 65 | MediaTrackVideo 66 | MediaTrackText 67 | ) 68 | 69 | // MediaTrackDescriptor contains information about a media track. 70 | type MediaTrackDescriptor struct { 71 | ID int // Media track identifier. 72 | Description string // Description of the media track. 73 | } 74 | 75 | // MediaAudioTrack contains information specific to audio media tracks. 76 | type MediaAudioTrack struct { 77 | Channels uint // number of audio channels. 78 | Rate uint // audio sample rate. 79 | } 80 | 81 | // MediaVideoTrack contains information specific to video media tracks. 82 | type MediaVideoTrack struct { 83 | Width uint // video width. 84 | Height uint // video height. 85 | Orientation VideoOrientation // video orientation. 86 | Projection VideoProjection // video projection mode. 87 | Pose VideoViewpoint // video initial viewpoint. 88 | 89 | // Aspect ratio information. 90 | AspectRatioNum uint // aspect ratio numerator. 91 | AspectRatioDen uint // aspect ratio denominator. 92 | 93 | // Frame rate information. 94 | FrameRateNum uint // frame rate numerator. 95 | FrameRateDen uint // frame rate denominator. 96 | } 97 | 98 | // MediaSubtitleTrack contains information specific to subtitle media tracks. 99 | type MediaSubtitleTrack struct { 100 | Encoding string // character encoding of the subtitle. 101 | } 102 | 103 | // MediaTrack contains information regarding a media track. 104 | type MediaTrack struct { 105 | ID int // Media track identifier. 106 | Type MediaTrackType // Media track type. 107 | BitRate uint // Media track bit rate. 108 | 109 | // libVLC representation of the four-character code of the codec used by 110 | // the media track. 111 | Codec uint 112 | 113 | // The original four-character code of the codec used by the media track, 114 | // extracted from the container. 115 | OriginalCodec uint 116 | 117 | // Codec profile (real audio flavor, MPEG audio layer, H264 profile, etc.). 118 | // NOTE: Profile values are codec specific. 119 | Profile int 120 | 121 | // Stream restriction level (resolution, bitrate, codec features, etc.). 122 | // NOTE: Level values are codec specific. 123 | Level int 124 | 125 | Language string // Media track language name. 126 | Description string // Description of the media track. 127 | 128 | // Type specific information. 129 | Audio *MediaAudioTrack 130 | Video *MediaVideoTrack 131 | Subtitle *MediaSubtitleTrack 132 | } 133 | 134 | // CodecDescription returns the description of the codec used by the media track. 135 | func (mt *MediaTrack) CodecDescription() (string, error) { 136 | if err := mt.assertInit(); err != nil { 137 | return "", err 138 | } 139 | 140 | codec := mt.Codec 141 | if codec == 0 { 142 | codec = mt.OriginalCodec 143 | } 144 | 145 | // Get codec description. 146 | return C.GoString(C.libvlc_media_get_codec_description( 147 | C.libvlc_track_type_t(mt.Type), 148 | C.uint(codec), 149 | )), nil 150 | } 151 | 152 | func (mt *MediaTrack) assertInit() error { 153 | if mt == nil { 154 | return ErrMediaTrackNotInitialized 155 | } 156 | 157 | return nil 158 | } 159 | 160 | func parseMediaTrack(cTrack *C.libvlc_media_track_t) (*MediaTrack, error) { 161 | if cTrack == nil { 162 | return nil, ErrMediaTrackNotInitialized 163 | } 164 | 165 | mt := &MediaTrack{ 166 | ID: int(cTrack.i_id), 167 | Type: MediaTrackType(cTrack.i_type), 168 | BitRate: uint(cTrack.i_bitrate), 169 | Codec: uint(cTrack.i_codec), 170 | OriginalCodec: uint(cTrack.i_original_fourcc), 171 | Profile: int(cTrack.i_profile), 172 | Level: int(cTrack.i_level), 173 | Language: C.GoString(cTrack.psz_language), 174 | Description: C.GoString(cTrack.psz_description), 175 | } 176 | 177 | switch mt.Type { 178 | case MediaTrackAudio: 179 | audio := *(**C.libvlc_audio_track_t)(unsafe.Pointer(&cTrack.anon0[0])) 180 | if audio == nil { 181 | break 182 | } 183 | 184 | mt.Audio = &MediaAudioTrack{ 185 | Channels: uint(audio.i_channels), 186 | Rate: uint(audio.i_rate), 187 | } 188 | case MediaTrackVideo: 189 | video := *(**C.libvlc_video_track_t)(unsafe.Pointer(&cTrack.anon0[0])) 190 | if video == nil { 191 | break 192 | } 193 | 194 | mt.Video = &MediaVideoTrack{ 195 | Width: uint(video.i_width), 196 | Height: uint(video.i_height), 197 | AspectRatioNum: uint(video.i_sar_num), 198 | AspectRatioDen: uint(video.i_sar_den), 199 | FrameRateNum: uint(video.i_frame_rate_num), 200 | FrameRateDen: uint(video.i_frame_rate_den), 201 | Orientation: VideoOrientation(video.i_orientation), 202 | Projection: VideoProjection(video.i_projection), 203 | Pose: VideoViewpoint{ 204 | Yaw: float64(video.pose.f_yaw), 205 | Pitch: float64(video.pose.f_pitch), 206 | Roll: float64(video.pose.f_roll), 207 | FOV: float64(video.pose.f_field_of_view), 208 | }, 209 | } 210 | case MediaTrackText: 211 | subtitle := *(**C.libvlc_subtitle_track_t)(unsafe.Pointer(&cTrack.anon0[0])) 212 | if subtitle == nil { 213 | break 214 | } 215 | 216 | mt.Subtitle = &MediaSubtitleTrack{ 217 | Encoding: C.GoString(subtitle.psz_encoding), 218 | } 219 | } 220 | 221 | return mt, nil 222 | } 223 | 224 | func parseMediaTrackDescriptorList(cDescriptors *C.libvlc_track_description_t) ([]*MediaTrackDescriptor, error) { 225 | if cDescriptors == nil { 226 | return nil, nil 227 | } 228 | 229 | var descriptors []*MediaTrackDescriptor 230 | for n := cDescriptors; n != nil; n = n.p_next { 231 | descriptors = append(descriptors, &MediaTrackDescriptor{ 232 | ID: int(n.i_id), 233 | Description: C.GoString(n.psz_name), 234 | }) 235 | } 236 | 237 | C.libvlc_track_description_list_release(cDescriptors) 238 | return descriptors, nil 239 | } 240 | -------------------------------------------------------------------------------- /v3/object_registry.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #include 4 | import "C" 5 | import ( 6 | "sync" 7 | "unsafe" 8 | ) 9 | 10 | type objectID = unsafe.Pointer 11 | 12 | type objectContext struct { 13 | refs uint 14 | data interface{} 15 | } 16 | 17 | type objectRegistry struct { 18 | sync.RWMutex 19 | 20 | contexts map[objectID]*objectContext 21 | } 22 | 23 | func newObjectRegistry() *objectRegistry { 24 | return &objectRegistry{ 25 | contexts: map[objectID]*objectContext{}, 26 | } 27 | } 28 | 29 | func (or *objectRegistry) get(id objectID) (interface{}, bool) { 30 | if id == nil { 31 | return nil, false 32 | } 33 | 34 | or.RLock() 35 | ctx, ok := or.contexts[id] 36 | or.RUnlock() 37 | 38 | if !ok { 39 | return nil, false 40 | } 41 | return ctx.data, ok 42 | } 43 | 44 | func (or *objectRegistry) add(data interface{}) objectID { 45 | or.Lock() 46 | 47 | var id objectID = C.malloc(C.size_t(1)) 48 | or.contexts[id] = &objectContext{ 49 | refs: 1, 50 | data: data, 51 | } 52 | 53 | or.Unlock() 54 | return id 55 | } 56 | 57 | func (or *objectRegistry) incRefs(id objectID) { 58 | if id == nil { 59 | return 60 | } 61 | 62 | or.Lock() 63 | 64 | ctx, ok := or.contexts[id] 65 | if ok { 66 | ctx.refs++ 67 | } 68 | 69 | or.Unlock() 70 | } 71 | 72 | func (or *objectRegistry) decRefs(id objectID) { 73 | if id == nil { 74 | return 75 | } 76 | 77 | or.Lock() 78 | 79 | ctx, ok := or.contexts[id] 80 | if ok { 81 | ctx.refs-- 82 | if ctx.refs == 0 { 83 | delete(or.contexts, id) 84 | C.free(id) 85 | } 86 | } 87 | 88 | or.Unlock() 89 | } 90 | -------------------------------------------------------------------------------- /v3/renderer.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | import "C" 6 | 7 | // RendererType represents the type of a renderer. 8 | type RendererType string 9 | 10 | // Renderer types. 11 | const ( 12 | RendererChromecast RendererType = "chromecast" 13 | ) 14 | 15 | // RendererFlags contains flags describing a renderer (e.g. capabilities). 16 | type RendererFlags struct { 17 | AudioEnabled bool 18 | VideoEnabled bool 19 | } 20 | 21 | // Renderer represents a medium capable of rendering media files. 22 | type Renderer struct { 23 | renderer *C.libvlc_renderer_item_t 24 | } 25 | 26 | // Name returns the name of the renderer. 27 | func (r *Renderer) Name() (string, error) { 28 | if err := r.assertInit(); err != nil { 29 | return "", nil 30 | } 31 | 32 | return C.GoString(C.libvlc_renderer_item_name(r.renderer)), nil 33 | } 34 | 35 | // Type returns the type of the renderer. 36 | func (r *Renderer) Type() (RendererType, error) { 37 | if err := r.assertInit(); err != nil { 38 | return "", nil 39 | } 40 | 41 | return RendererType(C.GoString(C.libvlc_renderer_item_type(r.renderer))), nil 42 | } 43 | 44 | // Flags returns the flags of the renderer. 45 | func (r *Renderer) Flags() (*RendererFlags, error) { 46 | if err := r.assertInit(); err != nil { 47 | return nil, err 48 | } 49 | 50 | flags := C.libvlc_renderer_item_flags(r.renderer) 51 | 52 | return &RendererFlags{ 53 | AudioEnabled: (flags | C.LIBVLC_RENDERER_CAN_AUDIO) != 0, 54 | VideoEnabled: (flags | C.LIBVLC_RENDERER_CAN_VIDEO) != 0, 55 | }, nil 56 | } 57 | 58 | // IconURI returns the icon URI of the renderer. 59 | func (r *Renderer) IconURI() (string, error) { 60 | if err := r.assertInit(); err != nil { 61 | return "", nil 62 | } 63 | 64 | return C.GoString(C.libvlc_renderer_item_icon_uri(r.renderer)), nil 65 | } 66 | 67 | func (r *Renderer) hold() { 68 | if err := r.assertInit(); err != nil { 69 | return 70 | } 71 | 72 | C.libvlc_renderer_item_hold(r.renderer) 73 | } 74 | 75 | func (r *Renderer) release() { 76 | if err := r.assertInit(); err != nil { 77 | return 78 | } 79 | 80 | C.libvlc_renderer_item_release(r.renderer) 81 | } 82 | 83 | func (r *Renderer) assertInit() error { 84 | if r == nil || r.renderer == nil { 85 | return ErrRendererNotInitialized 86 | } 87 | 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /v3/renderer_discoverer.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | // #include 6 | import "C" 7 | import ( 8 | "unsafe" 9 | ) 10 | 11 | // RendererDiscoveryCallback is used by renderer discovery services to 12 | // report discovery events. 13 | // 14 | // The available events are: 15 | // - RendererDiscovererItemAdded 16 | // - RendererDiscovererItemDeleted 17 | type RendererDiscoveryCallback func(Event, *Renderer) 18 | 19 | // RendererDiscovererDescriptor contains information about a renderer 20 | // discovery service. Pass the `Name` field to the NewRendererDiscoverer 21 | // method in order to create a new discovery service instance. 22 | type RendererDiscovererDescriptor struct { 23 | Name string 24 | LongName string 25 | } 26 | 27 | // ListRendererDiscoverers returns a list of descriptors identifying the 28 | // available renderer discovery services. 29 | func ListRendererDiscoverers() ([]*RendererDiscovererDescriptor, error) { 30 | if err := inst.assertInit(); err != nil { 31 | return nil, err 32 | } 33 | 34 | // Get renderer discoverer descriptors. 35 | var cDescriptors **C.libvlc_rd_description_t 36 | 37 | count := int(C.libvlc_renderer_discoverer_list_get(inst.handle, &cDescriptors)) 38 | if count <= 0 || cDescriptors == nil { 39 | return nil, nil 40 | } 41 | defer C.libvlc_renderer_discoverer_list_release(cDescriptors, C.size_t(count)) 42 | 43 | // Parse renderer discoverer descriptors. 44 | descriptors := make([]*RendererDiscovererDescriptor, 0, count) 45 | for i := 0; i < count; i++ { 46 | // Get current renderer discoverer descriptor. 47 | cDescriptorPtr := unsafe.Pointer(uintptr(unsafe.Pointer(cDescriptors)) + 48 | uintptr(i)*unsafe.Sizeof(*cDescriptors)) 49 | if cDescriptorPtr == nil { 50 | return nil, ErrRendererDiscovererParse 51 | } 52 | 53 | cDescriptor := *(**C.libvlc_rd_description_t)(cDescriptorPtr) 54 | if cDescriptor == nil { 55 | return nil, ErrRendererDiscovererParse 56 | } 57 | 58 | // Parse renderer discoverer descriptor. 59 | descriptors = append(descriptors, &RendererDiscovererDescriptor{ 60 | Name: C.GoString(cDescriptor.psz_name), 61 | LongName: C.GoString(cDescriptor.psz_longname), 62 | }) 63 | } 64 | 65 | return descriptors, nil 66 | } 67 | 68 | // RendererDiscoverer represents a renderer discovery service. 69 | // Discovery services use different discovery protocols (e.g. mDNS) 70 | // in order to find available media renderers (e.g. Chromecast). 71 | type RendererDiscoverer struct { 72 | discoverer *C.libvlc_renderer_discoverer_t 73 | renderers map[*C.libvlc_renderer_item_t]*Renderer 74 | stopFunc func() 75 | } 76 | 77 | // NewRendererDiscoverer instantiates the renderer discovery service 78 | // identified by the specified name. Use the ListRendererDiscoverers 79 | // method to obtain the list of available discovery service descriptors. 80 | // 81 | // NOTE: Call the Release method on the discovery service instance in 82 | // order to free the allocated resources. 83 | func NewRendererDiscoverer(name string) (*RendererDiscoverer, error) { 84 | if err := inst.assertInit(); err != nil { 85 | return nil, err 86 | } 87 | 88 | cName := C.CString(name) 89 | defer C.free(unsafe.Pointer(cName)) 90 | 91 | discoverer := C.libvlc_renderer_discoverer_new(inst.handle, cName) 92 | if discoverer == nil { 93 | return nil, errOrDefault(getError(), ErrRendererDiscovererCreate) 94 | } 95 | 96 | return &RendererDiscoverer{ 97 | discoverer: discoverer, 98 | renderers: map[*C.libvlc_renderer_item_t]*Renderer{}, 99 | }, nil 100 | } 101 | 102 | // Release stops and destroys the renderer discovery service along 103 | // with all the renderers found by the instance. 104 | func (rd *RendererDiscoverer) Release() error { 105 | if err := rd.assertInit(); err != nil { 106 | return nil 107 | } 108 | 109 | // Stop discovery service. 110 | rd.stop() 111 | 112 | // Release renderers. 113 | for _, renderer := range rd.renderers { 114 | renderer.release() 115 | } 116 | rd.renderers = nil 117 | 118 | // Release discovery service. 119 | C.libvlc_renderer_discoverer_release(rd.discoverer) 120 | rd.discoverer = nil 121 | 122 | return nil 123 | } 124 | 125 | // Start starts the renderer discovery service and reports discovery 126 | // events through the specified callback function. 127 | // 128 | // NOTE: The Stop and Release methods should not be called from the callback 129 | // function. Doing so will result in undefined behavior. 130 | func (rd *RendererDiscoverer) Start(cb RendererDiscoveryCallback) error { 131 | if cb == nil { 132 | return ErrInvalidEventCallback 133 | } 134 | 135 | // Stop discovery service, if started. 136 | if err := rd.Stop(); err != nil { 137 | return err 138 | } 139 | 140 | // Retrieve event manager. 141 | manager, err := rd.eventManager() 142 | if err != nil { 143 | return err 144 | } 145 | 146 | // Create event callback. 147 | eventCallback := func(event *C.libvlc_event_t, userData interface{}) { 148 | if err := rd.assertInit(); err != nil { 149 | return 150 | } 151 | if event == nil { 152 | return 153 | } 154 | 155 | cRenderer := *(**C.libvlc_renderer_item_t)(unsafe.Pointer(&event.u[0])) 156 | if cRenderer == nil { 157 | return 158 | } 159 | 160 | renderer, ok := rd.renderers[cRenderer] 161 | if !ok { 162 | renderer = &Renderer{renderer: cRenderer} 163 | renderer.hold() 164 | rd.renderers[cRenderer] = renderer 165 | } 166 | 167 | switch event := Event(event._type); event { 168 | case RendererDiscovererItemAdded: 169 | cb(event, renderer) 170 | case RendererDiscovererItemDeleted: 171 | cb(event, renderer) 172 | delete(rd.renderers, cRenderer) 173 | renderer.release() 174 | } 175 | } 176 | 177 | // Attach discovery service events. 178 | events := []Event{ 179 | RendererDiscovererItemAdded, RendererDiscovererItemDeleted, 180 | } 181 | 182 | eventIDs := make([]EventID, 0, len(events)) 183 | for _, event := range events { 184 | eventID, err := manager.attach(event, nil, eventCallback, nil) 185 | if err != nil { 186 | return err 187 | } 188 | 189 | eventIDs = append(eventIDs, eventID) 190 | } 191 | defer func() { 192 | if rd.stopFunc == nil { 193 | manager.Detach(eventIDs...) 194 | } 195 | }() 196 | 197 | // Start discovery service. 198 | if C.libvlc_renderer_discoverer_start(rd.discoverer) < 0 { 199 | return errOrDefault(getError(), ErrRendererDiscovererStart) 200 | } 201 | 202 | rd.stopFunc = func() { 203 | // Detach events. 204 | manager.Detach(eventIDs...) 205 | 206 | // Stop discovery service. 207 | C.libvlc_renderer_discoverer_stop(rd.discoverer) 208 | } 209 | 210 | return nil 211 | } 212 | 213 | // Stop stops the discovery service. 214 | func (rd *RendererDiscoverer) Stop() error { 215 | if err := rd.assertInit(); err != nil { 216 | return err 217 | } 218 | 219 | rd.stop() 220 | return nil 221 | } 222 | 223 | func (rd *RendererDiscoverer) stop() { 224 | if rd.stopFunc != nil { 225 | rd.stopFunc() 226 | rd.stopFunc = nil 227 | } 228 | } 229 | 230 | // eventManager returns the event manager responsible for the renderer 231 | // discovery service. 232 | func (rd *RendererDiscoverer) eventManager() (*EventManager, error) { 233 | if err := rd.assertInit(); err != nil { 234 | return nil, err 235 | } 236 | 237 | manager := C.libvlc_renderer_discoverer_event_manager(rd.discoverer) 238 | if manager == nil { 239 | return nil, ErrMissingEventManager 240 | } 241 | 242 | return newEventManager(manager), nil 243 | } 244 | 245 | func (rd *RendererDiscoverer) assertInit() error { 246 | if rd == nil || rd.discoverer == nil { 247 | return ErrRendererDiscovererNotInitialized 248 | } 249 | 250 | return nil 251 | } 252 | -------------------------------------------------------------------------------- /v3/utils.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | // #include 6 | import "C" 7 | import ( 8 | "errors" 9 | "net/url" 10 | "path/filepath" 11 | "runtime" 12 | "strings" 13 | ) 14 | 15 | func getError() error { 16 | msg := C.libvlc_errmsg() 17 | if msg == nil { 18 | return nil 19 | } 20 | 21 | err := errors.New(C.GoString(msg)) 22 | C.libvlc_clearerr() 23 | return err 24 | } 25 | 26 | func errOrDefault(err, defaultErr error) error { 27 | if err != nil { 28 | return err 29 | } 30 | 31 | return defaultErr 32 | } 33 | 34 | func boolToInt(value bool) int { 35 | if value { 36 | return 1 37 | } 38 | 39 | return 0 40 | } 41 | 42 | func urlToPath(mrl string) (string, error) { 43 | url, err := url.Parse(mrl) 44 | if err != nil { 45 | return "", err 46 | } 47 | if url.Scheme != "file" { 48 | return mrl, nil 49 | } 50 | path := filepath.Clean(url.Path) 51 | 52 | if runtime.GOOS == "windows" { 53 | sep := string(filepath.Separator) 54 | if url.Host != "" { 55 | path = strings.Repeat(sep, 2) + filepath.Join(url.Host, path) 56 | } else { 57 | path = strings.TrimLeft(path, sep) 58 | } 59 | } 60 | 61 | return path, nil 62 | } 63 | -------------------------------------------------------------------------------- /v3/version.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #include 5 | // #include 6 | import "C" 7 | import "fmt" 8 | 9 | // VersionInfo contains details regarding the version of the libVLC module. 10 | type VersionInfo struct { 11 | Major uint 12 | Minor uint 13 | Patch uint 14 | Extra uint 15 | } 16 | 17 | // String returns a string representation of the version. 18 | func (v VersionInfo) String() string { 19 | return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) 20 | } 21 | 22 | // Runtime returns the runtime version of libVLC, usually including 23 | // the codename of the build. 24 | // 25 | // NOTE: Due to binary backward compatibility, the runtime version may be 26 | // more recent than the build version. 27 | func (v VersionInfo) Runtime() string { 28 | return C.GoString(C.libvlc_get_version()) 29 | } 30 | 31 | // Changeset returns the changeset identifier for the current libVLC build. 32 | func (v VersionInfo) Changeset() string { 33 | return C.GoString(C.libvlc_get_changeset()) 34 | } 35 | 36 | // Compiler returns information regarding the compiler used to build libVLC. 37 | func (v VersionInfo) Compiler() string { 38 | return C.GoString(C.libvlc_get_compiler()) 39 | } 40 | 41 | var moduleVersion = VersionInfo{ 42 | Major: C.LIBVLC_VERSION_MAJOR, 43 | Minor: C.LIBVLC_VERSION_MINOR, 44 | Patch: C.LIBVLC_VERSION_REVISION, 45 | Extra: C.LIBVLC_VERSION_EXTRA, 46 | } 47 | -------------------------------------------------------------------------------- /v3/vlc.go: -------------------------------------------------------------------------------- 1 | package vlc 2 | 3 | // #cgo LDFLAGS: -lvlc 4 | // #cgo CFLAGS: -w 5 | // #include 6 | // #include 7 | import "C" 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | type instance struct { 13 | handle *C.libvlc_instance_t 14 | events *eventRegistry 15 | objects *objectRegistry 16 | } 17 | 18 | func (i *instance) assertInit() error { 19 | if i == nil || i.handle == nil { 20 | return ErrModuleNotInitialized 21 | } 22 | 23 | return nil 24 | } 25 | 26 | var inst *instance 27 | 28 | // Init creates an instance of the libVLC module. 29 | // Must be called only once and the module instance must be released using 30 | // the Release function. 31 | func Init(args ...string) error { 32 | if inst != nil { 33 | return nil 34 | } 35 | 36 | argc := len(args) 37 | argv := make([]*C.char, argc) 38 | 39 | for i, arg := range args { 40 | argv[i] = C.CString(arg) 41 | } 42 | defer func() { 43 | for i := range argv { 44 | C.free(unsafe.Pointer(argv[i])) 45 | } 46 | }() 47 | 48 | handle := C.libvlc_new(C.int(argc), *(***C.char)(unsafe.Pointer(&argv))) 49 | if handle == nil { 50 | return errOrDefault(getError(), ErrModuleInitialize) 51 | } 52 | 53 | inst = &instance{ 54 | handle: handle, 55 | events: newEventRegistry(), 56 | objects: newObjectRegistry(), 57 | } 58 | 59 | return nil 60 | } 61 | 62 | // Release destroys the instance created by the Init function. 63 | func Release() error { 64 | if inst == nil { 65 | return nil 66 | } 67 | 68 | C.libvlc_release(inst.handle) 69 | inst = nil 70 | return nil 71 | } 72 | 73 | // Version returns details regarding the version of the libVLC module. 74 | func Version() VersionInfo { 75 | return moduleVersion 76 | } 77 | 78 | // SetAppName sets the human-readable application name and the HTTP user agent. 79 | // The specified user agent is used when a protocol requires it. 80 | func SetAppName(name, userAgent string) error { 81 | if err := inst.assertInit(); err != nil { 82 | return err 83 | } 84 | 85 | cName, cUserAgent := C.CString(name), C.CString(userAgent) 86 | C.libvlc_set_user_agent(inst.handle, cName, cUserAgent) 87 | 88 | C.free(unsafe.Pointer(cName)) 89 | C.free(unsafe.Pointer(cUserAgent)) 90 | return nil 91 | } 92 | 93 | // SetAppID sets metadata for identifying the application. 94 | func SetAppID(id, version, icon string) error { 95 | if err := inst.assertInit(); err != nil { 96 | return err 97 | } 98 | 99 | cID, cVersion, cIcon := C.CString(id), C.CString(version), C.CString(icon) 100 | C.libvlc_set_app_id(inst.handle, cID, cVersion, cIcon) 101 | 102 | C.free(unsafe.Pointer(cID)) 103 | C.free(unsafe.Pointer(cVersion)) 104 | C.free(unsafe.Pointer(cIcon)) 105 | return nil 106 | } 107 | 108 | // StartUserInterface attempts to start a user interface for the libVLC 109 | // instance. Pass an empty string as the name parameter in order to start 110 | // the default interface. 111 | func StartUserInterface(name string) error { 112 | if err := inst.assertInit(); err != nil { 113 | return err 114 | } 115 | 116 | cName := C.CString(name) 117 | defer C.free(unsafe.Pointer(cName)) 118 | 119 | if C.libvlc_add_intf(inst.handle, cName) < 0 { 120 | return errOrDefault(getError(), ErrUserInterfaceStart) 121 | } 122 | 123 | return nil 124 | } 125 | --------------------------------------------------------------------------------