Python bindings for libVLC - cannot change audio output device - python

VLC 2.2.3, python-vlc 1.1.2.
I have a virtual audio output and am trying to get libVLC to output on it. So far, I can see the virtual output appearing in libVLC, but selecting it makes audio play on the default output (i.e. the speakers).
This is the relevant part of what I have:
self.Instance = vlc.Instance()
self.player = self.Instance.media_player_new()
devices = []
mods = self.player.audio_output_device_enum()
if mods:
mod = mods
while mod:
mod = mod.contents
devices.append(mod.device)
mod = mod.next
vlc.libvlc_audio_output_device_list_release(mods)
# this is the part I change on each run of the code.
self.player.audio_output_device_set(None, devices[0])
I've run the code multiple times, changing the device ID as per the code comment. However, the output device doesn't actually change. This is a headache for two reasons:
1) audio_output_device_set() doesn't return anything. I can't tell if I'm actually accomplishing anything when I run this function.
2) I can't even run audio_output_device_get() to check if the set function is doing anything as this is only for libvlc 3. I would prefer for my program to work with 2.2.3.
So, what I did next was install VLC 3.0 and run the above code with it. Now, audio_output_device_get() works and I can see that the set function is actually changing the output device to the virtual output. But sound STILL plays on the speakers.
What's going on? How do I fix this?
I asked at the VLC forums and got a singularly unhelpful reply telling me to 'check logs and documentation'. That's it. I've been superglued to the rather lacking documentation to get this far. Even though I doubt it can help, I've decided to try logging. I thought it would be as simple as calling libvlc_log_set_file but it needs a libVLC file pointer, and I don't know how to create one with a name and mode as in Python.
tl;dr:
1) How do I successfully change the audio output device?
2) How do I set up maximum verbosity logging?

1) For some reason, I had to pause and unpause before VLC would register my change.
This code fixes things:
[... rest of GUI class ...]
self.p.play()
self.root.after(350, self.DeviceSet)
def DeviceSet(self):
self.p.audio_output_device_set(None, self.audiodevice)
self.p.pause()
self.root.after(10)
self.p.pause()
2) Initialise VLC as follows:
self.instance = vlc.Instance('--verbose 9')

Here is a full example of how to switch to different audio device.
Remember: don't call player.stop() after player.audio_output_device_set(), otherwise the set operation won't work!!
import time
from typing import List
import vlc
def vlc_set_device_test(filename: str):
# creating a vlc instance
vlc_instance: vlc.Instance = vlc.Instance()
player: vlc.MediaPlayer = vlc_instance.media_player_new()
media: vlc.Media = vlc_instance.media_new(filename)
player.set_media(media)
# list devices
device_ids: List[bytes] = []
mods = player.audio_output_device_enum()
if mods:
index = 0
mod = mods
while mod:
mod = mod.contents
desc = mod.description.decode('utf-8', 'ignore')
print(f'index = {index}, desc = {desc}')
device_ids.append(mod.device)
mod = mod.next
index += 1
# free devices
vlc.libvlc_audio_output_device_list_release(mods)
# hard code device
pc_speaker = device_ids[1]
headset = device_ids[3]
# play music to default device
player.play()
time.sleep(3)
# set output device
player.audio_output_device_set(None, headset)
# don't call player.stop()!!
player.pause()
# now music is playing from headset
player.play()
time.sleep(10)
player.stop()
if __name__ == '__main__':
vlc_set_device_test(r'D:\cheer up.mp3')

Related

Interfacing MIDI device (Akai LPD8 mk2) from Python - works for reading but not sending

Preface
I have an Akai LPD8 mk2 which I would like to interface from Python.
There are two projects that (I believe) provide interfaces for the predecessor (mk1):
https://github.com/zetof/LPD8
https://github.com/DrLuke/python-lpd8
I found I needed to make a few small changes to get the reading to work with these libs (*example below, not sure how relevant this is and it could easily be because I misunderstand something).
However, no matter what I tried, I could not get the sending to the device to work. Specifically, changing the state (on/off) of the pads of the device from Python. This state is indicated by the color of the pads' lighting.
Question
A minimal example of what I thought should work is the following:
With the very nice mido library, I would like to read a few state changes of the pads. Then send them back with a small delay (similar idea here: https://stackoverflow.com/a/29501455/655404):
from time import sleep
import mido
def get_ioport_name(search):
# see footnote **
names = mido.get_ioport_names()
names = set(n for n in names if search in n)
assert len(names) == 1
return names.pop()
def get_messages(port):
return list(port.iter_pending())
name = get_ioport_name("LPD8")
port = mido.open_ioport(name) # see footnote ***
# ensure that there are no messages in the queue
get_messages(port)
#pause
input("Press a few pads, then press Enter to continue...")
msgs = get_messages(port)
print(f"Recorded these messages:\n{msgs}\n")
print("Echoing these messages:")
for m in msgs:
print(m)
port.send(m)
sleep(1)
This runs and gives no error messages. However, the expected color changes on the pads do not happen.
What am I doing wrong?
I have tried with different backends for mido (rtmidi, portmidi, pygame) as well as using rtmidi directly, all with the same result.
Footnotes
*Example for changes needed:
I had to change the constant's block here https://github.com/zetof/LPD8/blob/master/lpd8/lpd8.py#L16 to:
NOTE_ON = 144+9
NOTE_OFF = 128+9
CTRL = 176
PGM_CHG = 192+9
**Port names
names = mido.get_ioport_names() gives:
['Midi Through:Midi Through Port-0 14:0',
'LPD8 mk2:LPD8 mk2 MIDI 1 20:0',
'Midi Through:Midi Through Port-0 14:0',
'LPD8 mk2:LPD8 mk2 MIDI 1 20:0']
and set(names) gives:
{'LPD8 mk2:LPD8 mk2 MIDI 1 20:0', 'Midi Through:Midi Through Port-0 14:0'}
The result is also the same for mido.get_input_names() and mido.get_output_names().
***Ports in mido
From what I understand mido has three port classes:
open_input() # only receive
open_output() # only send
open_ioport() # both (and what I used above)
If I change from ioport to using an input/output pair, the result is the same:
port_in = mido.open_input(name)
port_out = mido.open_output(name)
# ensure that there are no messages in the queue
get_messages(port_in)
#pause
input("Press a few pads, then press Enter to continue...")
msgs = get_messages(port_in)
print(f"Recorded these messages:\n{msgs}\n")
print("Echoing these messages:")
for m in msgs:
print(m)
port_out.send(m)
sleep(1)

How to change VLC-python output device?

In my project, I need to get sound from a radio stream URL and play it. But it needs to be played in a specific output device called "VB-Cable Input (Virtual Audio Cable)".
I couldn't find a way to do it. This is my current code:
import vlc
import time
url = "https://shout25.crossradio.com.br:18156/1"
# Create VLC instance, media player and media
instance = vlc.Instance()
player = instance.media_player_new()
media = instance.media_new(url)
player.set_media(media)
# Get list of output devices
def get_device():
mods = player.audio_output_device_enum()
if mods:
mod = mods
while mod:
mod = mod.contents
# If VB-Cable is found, return it's module and device id
if 'CABLE Input (VB-Audio Virtual Cable)' in str(mod.description):
device = mod.device
module = mod.description
return device,module
mod = mod.next
# Sets the module and device id to VB-Cable
device,module = get_device()
# Sets VB-Cable as output device
player.audio_output_device_set(device,module)
# Trying the function #Random Davis stated, but results still the same
vlc.libvlc_audio_output_device_set(player, module, device)
# It should return VB-Cable as current audio output, but None is returned...
print(player.audio_output_device_get())
# Audio plays in default output device, which is incorrect
player.play()
time.sleep(60)
I searched through StackOverflow and the VLC documentation, but couldn't get the result I expected, I believe I'm using the functions in the wrong way.
After some research, I found this implementation of audio_output_device_set() in GitHub
As #RandomDavis said, the problem was indeed in this function. All I had to do was switching the first argument to None and leave the rest as it is.
This is the correct code:
# Sets VB-Cable as output device
player.audio_output_device_set(None, device)
Still not quite sure why inserting any values other than None makes the function invalid. But the problem is now solved.

Python Code not opening VlC player of twitch stream instances

Hello so I don't stream right but wanted to make a video on peoples reactions when they are suddenly hit with a lot of people (this would be accompanied by a chat bot too and ill tell them what it was as well as ask for use permissions). So I thought it would be fun to look at view bots for twitch and found one online (code below). so I ran in installed streamlink via Pip and windows executable and it seems to run "found matching plugin twitch for URL "Stream link"" but it doesn't actually increase viewership and I can only assume this is because its not actually opening the Vlc instances, so here I am wondering what I need to do I have the latest version of python and git isnt trying to download and install anything so im assuming streamlink is all I need but im kind confused why it woudnt be opening the VLC instance any help is most appreciated.
Edit: oh and I do have the proxies and using a small amount to try and get it to work first, and will buy more later but after I get this to work!
import concurrent.futures, time, random, os
#desired channel url
channel_url = 'https://www.twitch.tv/StreamerName'
#number of viewer bots
botcount = 10
#path to proxies.txt file
proxypath = "C:\Proxy\proxy.txt"
#path to vlc
playerpath = r'"C:\Program Files\VideoLAN\VLC\vlc.exe"'
#takes proxies from proxies.txt and returns to list
def create_proxy_list(proxyfile, shared_list):
with open(proxyfile, 'r') as file:
proxies = [line.strip() for line in file]
for i in proxies:
shared_list.append((i))
return shared_list
#takes random proxies from the proxies list and adds them to another list
def randproxy(proxylist, botcount):
randomproxylist = list()
for _ in range(botcount):
proxy = random.choice(proxylist)
randomproxylist.append(proxy)
proxylist.remove(proxy)
return (randomproxylist)
#launches a viewer bot after a short delay
def launchbots(proxy):
time.sleep(random.randint(5, 10))
os.system(f'streamlink --player={playerpath} --player-no-close --player-http --hls-segment-timeout 30 --hls-segment-attempts 3 --retry-open 1 --retry-streams 1 --retry-max 1 --http-stream-timeout 3600 --http-proxy {proxy} {channel_url} worst')
#calls the launchbots function asynchronously
def main(randomproxylist):
with concurrent.futures.ThreadPoolExecutor() as executer:
executer.map(launchbots, randomproxylist)
if __name__ == "__main__":
main(randproxy(create_proxy_list(proxypath, shared_list=list()), botcount))

How to get Spotify current playing song in Python (Windows)?

I want to show the current playing song in Spotify on a 16x2 LCD.
I was thinking of connecting the LCD with my Arduino and then making a Python script that sends the current playing song of Spotify to the Arduino.
To get to the point, I'm looking for a way to get Spotify's current playing song in Python. (I'm using Windows 8.) I found some ways like dbus, but they were either for Linux or for Mac.
Thanks in advance! (And sorry for bad English grammar.)
I encountered the same issue, so I wrote a library to solve this issue. The library can be found at github: https://github.com/XanderMJ/spotilib. Keep in mind that this is still work in progress.
Just copy the file and place it in your Python/Lib directory.
import spotilib
spotilib.artist() #returns the artist of the current playing song
spotilib.song() #returns the song title of the current playing song
spotilib.artist() returns only the first artist. I started working on an other library spotimeta.py to solve this issue. However, this is not working at 100% yet.
import spotimeta
spotimeta.artists() #returns a list of all the collaborating artists of the track
If an error occurs, spotimeta.artists() will return only the first artist (found with spotilib.artist())
Hope this will help you (if still needed)!
The easiest way would probably be to scrobble the currently playing tracks from the Spotify client to a last.fm account and then use python to get it from there.
Last.fm allows you to get scrobbled tracks via their api with user.getRecentTracks which provides the nowplaying="true" attribute if a song is playing. It also provides some other useful things you may want for an external display like a link to the album art and last.fm page for the song.
Here's a quick example that takes a username and api key as cmd line arguments and fetches what is currently playing for that user using the requests library.
from time import sleep
import requests
import json
from pprint import pprint
import sys
def get_playing():
base_url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user='
user = sys.argv[1]
key = sys.argv[2]
r = requests.get(base_url+user+'&api_key='+key+'&format=json')
data = json.loads(r.text)
latest_track = data['recenttracks']['track'][0]
try:
if latest_track['#attr']['nowplaying'] == 'true':
artist = latest_track['artist']['#text']
song = latest_track['name']
album = latest_track['album']['#text']
print "\nNow Playing: {0} - {1}, from the album {2}.\n".format(artist, song, album)
except KeyError:
print '\nNothing playing...\n'
def main():
if len(sys.argv)<3:
print "\nError: Please provide a username and api key in the format of: 'scrobble.py username api_key\n"
else:
get_playing()
if __name__ == '__main__':
main()
In a quick test it does seem to take a minute or so to realize the track is no longer playing after pausing or exiting the Spotify client however.
Is there more than one pytify ?
This worked for me until today:
https://code.google.com/p/pytify/
Spotify has been updated, now song is not shown in windows title anymore.
They will bring the "feature" back:
https://community.spotify.com/t5/ideas/v2/ideapage/blog-id/ideaexchange/article-id/73238/page/2#comments
while True:
print("Welcome to the project, " + user_name['display_name'])
print("0 - Exit the console")
print("1 - Search for a Song")
user_input = int(input("Enter Your Choice: "))
if user_input == 1:
search_song = input("Enter the song name: ")
results = spotifyObject.search(search_song, 1, 0, "track")
songs_dict = results['tracks']
song_items = songs_dict['items']
song = song_items[0]['external_urls']['spotify']
webbrowser.open(song)
print('Song has opened in your browser.')
elif user_input == 0:
print("Good Bye, Have a great day!")
break
else:
print("Please enter valid user-input.")

Play mp4 video with python and gstreamer

I'm trying to play video in mp4 format but not working.
In console I execute this line and it works:
gst-launch playbin uri=rtmp://localhost:1935/files/video.mp4
But if I change to version 1.0 only works the audio:
gst-launch-1.0 playbin uri=rtmp://localhost:1935/files/video.mp4
in python I have the following code:
self.player = Gst.Pipeline.new("player")
source = Gst.ElementFactory.make("filesrc", "file-source")
demuxer = Gst.ElementFactory.make("mp4mux", "demuxer")
demuxer.connect("pad-added", self.demuxer_callback)
self.video_decoder = Gst.ElementFactory.make("x264enc", "video-decoder")
self.audio_decoder = Gst.ElementFactory.make("vorbisdec", "audio-decoder")
audioconv = Gst.ElementFactory.make("audioconvert", "converter")
audiosink = Gst.ElementFactory.make("autoaudiosink", "audio-output")
videosink = Gst.ElementFactory.make("autovideosink", "video-output")
self.queuea = Gst.ElementFactory.make("queue", "queuea")
self.queuev = Gst.ElementFactory.make("queue", "queuev")
colorspace = Gst.ElementFactory.make("videoconvert", "colorspace")
self.player.add(source)
self.player.add(demuxer)
self.player.add(self.video_decoder)
self.player.add(self.audio_decoder)
self.player.add(audioconv)
self.player.add(audiosink)
self.player.add(videosink)
self.player.add(self.queuea)
self.player.add(self.queuev)
self.player.add(colorspace)
source.link(demuxer)
self.queuev.link(self.video_decoder)
self.video_decoder.link(colorspace)
colorspace.link(videosink)
self.queuea.link(self.audio_decoder)
self.audio_decoder.link(audioconv)
audioconv.link(audiosink)
but I get this error:
Error: Error in the internal data flow. gstbasesrc.c(2865): gst_base_src_loop (): /GstPipeline:player/GstFileSrc:file-source:
streaming task paused, reason not-linked (-1)
What can be happening? think I am no good decoding
You are missing linking the demuxer pads to your queues. Demuxers have 'sometimes' pads so you need to listen to the pad-added signal of them and link in this callback. Remember to check the pad caps once you get them and link to the appropriate branch of your pipeline.
You can read about dynamic pads here: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/chapter-pads.html#section-pads-dynamic
You have in your code:
demuxer = Gst.ElementFactory.make("mp4mux", "demuxer")
demuxer.connect("pad-added", self.demuxer_callback)
I hope this is a cut/paste error, as demuxing with a mux will not work. I believe for an .mp4 file, the normal demuxer (if you are choosing one by hand) is qtdemux.
You could also use decodebin to decode the file for you.

Categories