I was trying to Play multiple small mp4 files that is in a folder. And i was using this example as my reference:
https://github.com/oaubert/python-vlc/blob/master/generated/3.0/examples/play_buffer.py
But when i ran the py file giving that particular folder it read all of the files but closes after playing the first file. Can Someone Help me with this.
And i know i can just use a for loop to play all the files in a folder but i don't want any stutter between transitioning those files(Or in simple words i want seamless transition between those files). So any help would be appreciated.
i don't want any stutter between transitioning those files
What you are looking for is called "gapless playback". Sadly, this is not currently supported by LibVLC.
Regardless, to play multiple individual files, you need to either feed them one after the other to the mediaplayer (by subscribing to the EndReached event, for example) or use a MediaList object.
You have picked a complicated and arguably inappropriate code example for your scenario.
Try something like this (without ctypes):
import vlc
import glob
import time
base_folder = './'
# vlc State 0: Nowt, 1 Opening, 2 Buffering, 3 Playing, 4 Paused, 5 Stopped, 6 Ended, 7 Error
playing = set([1,2,3,4])
def add_media(inst, media_list, playlist):
for song in playlist:
print('Loading: - {0}'.format(song))
media = inst.media_new(song)
media_list.add_media(media)
playlist = glob.glob(base_folder + "/" + "*.mp3")
playlist = sorted(playlist)
media_player = vlc.MediaListPlayer()
inst = vlc.Instance('--no-xlib --quiet ')
media_list = vlc.MediaList()
add_media(inst, media_list, playlist)
media_player.set_media_list(media_list)
media_player.play()
time.sleep(0.1)
current = ""
idx = 1
player = media_player.get_media_player()
while True:
state = player.get_state()
if state.value == vlc.State.Ended or state == vlc.State.Error:
if idx == len(playlist)+1:
break
title = player.get_media().get_mrl()
if title != current:
print("\nPlaying - {0}\t{1} of {2}".format(str(title), idx, len(playlist)))
current = title
idx += 1
time.sleep(0.1)
print("\nPlaylist Finished")
Here, we use a MediaListPlayer() rather than a media.player_new_from_media() or a media_player_new() because we are playing a set of media, not a single instance.
You can improve on this by controlling it via events using the event_manager() and attaching appropriate events for the player to listen for.
Related
I have a small group of Raspberry Pis, all on the same local network (192.168.1.2xx) All are running Python 3.7.3, one (R Pi CM3) on Raspbian Buster, the other (R Pi 4B 8gig) on Raspberry Pi OS 64.
I have a file on one device (the Pi 4B), located at /tmp/speech.wav, that is generated on the fly, real-time:
192.168.1.201 - /tmp/speech.wav
I have a script that works well on that device, that tells me the play duration time of the .wav file in seconds:
import wave
import contextlib
def getPlayTime():
fname = '/tmp/speech.wav'
with contextlib.closing(wave.open(fname,'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = round(frames / float(rate), 2)
return duration
However - the node that needs to operate on that duration information is running on another node at 192.168.1.210. I cannot simply move the various files all to the same node as there is a LOT going on, things are where they are for a reason.
So what I need to know is how to alter my approach such that I can change the script reference to something like this pseudocode:
fname = '/tmp/speech.wav # 192.168.1.201'
Is such a thing possible? Searching the web it seems that I am up against millions of people looking for how to obtain IP addresses, fix multiple IP address issues, fix duplicate ip address issues... but I can't seem yet to find how to simply examine a file on a different ip address as I have described here. I have no network security restrictions, so any setting is up for consideration. Help would be much appreciated.
There are lots of possibilities, and it probably comes down to how often you need to check the duration, from how many clients, and how often the file changes and whether you have other information that you want to share between the nodes.
Here are some options:
set up an SMB (Samba) server on the Pi that has the WAV file and let the other nodes mount the filesystem and access the file as if it was local
set up an NFS server on the Pi that has the WAV file and let the other nodes mount the filesystem and access the file as if it was local
let other nodes use ssh to login and extract the duration, or scp to retrieve the file - see paramiko in Python
set up Redis on one node and throw the WAV file in there so anyone can get it - this is potentially attractive if you have lots of lists, arrays, strings, integers, hashes, queues or sets that you want to share between Raspberry Pis very fast. Example here.
Here is a very simple example of writing a sound track into Redis from one node (say Redis is on 192.168.0.200) and reading it back from any other. Of course, you may just want the writing node to write the duration in there rather than the whole track - which would be more efficient. Or you may want to store loads of other shared data or settings.
This is the writer:
#!/usr/bin/env python3
import redis
from pathlib import Path
host='192.168.1.200'
# Connect to Redis
r = redis.Redis(host)
# Load some music, or otherwise create it
music = Path('song.wav').read_bytes()
# Put music into Redis where others can see it
r.set("music",music)
And this is the reader:
#!/usr/bin/env python3
import redis
from pathlib import Path
host='192.168.1.200'
# Connect to Redis
r = redis.Redis(host)
# Retrieve music track from Redis
music = r.get("music")
print(f'{len(music)} bytes read from Redis')
Then, during testing, you may want to manually push a track into Redis from the Terminal:
redis-cli -x -h 192.168.0.200 set music < OtherTrack.wav
Or manually retrieve the track from Redis to a file:
redis-cli -h 192.168.0.200 get music > RetrievedFromRedis.wav
OK, this is what I finally settled on - and it works great. Using ZeroMQ for message passing, I have the function to get the playtime of the wav, and another gathers data about the speech about to be spoken, then all that is sent to the motor core prior to sending the speech. The motor core handles the timing issues to sync the jaw to the speech. So, I'm not actually putting the code that generates the wav and also returns the length of the wav playback time onto the node that ultimately makes use of it, but it turns out that message passing is fast enough so there is plenty of time space to receive, process and implement the motion control to match the speech perfectly. Posting this here in case it's helpful for folks in the future working on similar issues.
import time
import zmq
import os
import re
import wave
import contextlib
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555") #Listens for speech to output
print("Connecting to Motor Control")
jawCmd = context.socket(zmq.PUB)
jawCmd.connect("tcp://192.168.1.210:5554") #Sends to MotorFunctions for Jaw Movement
def getPlayTime(): # Checks to see if current file duration has changed
fname = '/tmp/speech.wav' # and if yes, sends new duration
with contextlib.closing(wave.open(fname,'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = round(frames / float(rate), 3)
speakTime = str(duration)
return speakTime
def set_voice(V,T):
T2 = '"' + T + '"'
audioFile = "/tmp/speech.wav" # /tmp set as tmpfs, or RAMDISK to reduce SD Card write ops
if V == "A":
voice = "Allison"
elif V == "B":
voice = "Belle"
elif V == "C":
voice = "Callie"
elif V == "D":
voice = "Dallas"
elif V == "V":
voice = "David"
else:
voice = "Belle"
os.system("swift -n " + voice + " -o " + audioFile + " " +T2) # Record audio
tailTrim = .5 # Calculate Jaw Timing
speakTime = eval(getPlayTime()) # Start by getting playlength
speakTime = round((speakTime - tailTrim), 2) # Chop .5 s for trailing silence
wordList = T.split()
jawString = []
for index in range(len(wordList)):
wordLen = len(wordList[index])
jawString.append(wordLen)
jawString = str(jawString)
speakTime = str(speakTime)
jawString = speakTime + "|" + jawString # 3.456|[4, 2, 7, 4, 2, 9, 3, 4, 3, 6] - will split on "|"
jawCmd.send_string(jawString) # Send Jaw Operating Sequence
os.system("aplay " + audioFile) # Play audio
pronunciationDict = {'teh':'the','process':'prawcess','Maeve':'Mayve','Mariposa':'May-reeposah','Lila':'Lala','Trump':'Ass hole'}
def adjustResponse(response): # Adjusts spellings in output string to create better speech output.
for key, value in pronunciationDict.items():
if key in response or key.lower() in response:
response = re.sub(key, value, response, flags=re.I)
return response
SpeakText="Speech center connected and online."
set_voice(V,SpeakText) # Cepstral Voices: A = Allison; B = Belle; C = Callie; D = Dallas; V = David;
while True:
SpeakText = socket.recv().decode('utf-8') # .decode gets rid of the b' in front of the string
SpeakTextX = adjustResponse(SpeakText) # Run the string through the pronunciation dictionary
print("SpeakText = ",SpeakTextX)
set_voice(V,SpeakTextX)
print("Received request: %s" % SpeakTextX)
socket.send_string(str(SpeakTextX)) # Send data back to source for confirmation
I'm looking for playing a video with vlc player via python3.8. I'm able to play a movie (mp4) but I would like to add additional audio tracks. I read that the 'add slave' method is the (new) way but I'm not able to use it properly: I'm not able to add subtitles nor audio track.
To summerize: what I want to achieve with Python is roughly the following:
https://wiki.videolan.org/VLC_HowTo/Play_an_external_audio_track_for_a_video
my current (non working) snippet:
import vlc
base_path = r"Z:/test/libvlc/"
video_file = base_path + "original.mp4"
audio_file = base_path + "2xlcDLHY7k0-instru+vocal_stereo.wav"
sub_file = base_path + "word.ass"
Instance = vlc.Instance()
player = Instance.media_player_new()
Media = Instance.media_new(video_file)
AdditionalTrack = player.add_slave(player, audio_file, True, i_type="audio")
Sub = player.add_slave(player,sub_file, True)
player.set_media(Media)
while True:
player.play()
I found doc for 'add_slave' func here:
https://www.olivieraubert.net/vlc/python-ctypes/doc/
but I'm not able to use it properly
libvlc_media_slaves_add(p_md, i_type, i_priority, psz_uri)
Add a slave to the current media. A slave is an external input source that may contains an additional subtitle track (like a .srt) or an additional audio track (like a .ac3).
Parameters: p_md - media descriptor object. i_type - subtitle or audio. i_priority - from 0 (low priority) to 4 (high priority). psz_uri - Uri of the slave (should contain a valid scheme).
If anyone know how to add subtitles or additional audio track,
I would be grateful to him if he can advise me how to,
thanks a lot !
use this code instead.
AdditionalTrack = player.add_slave(player, audio_file, True, i_type=vlc.MediaSlaveType(1))
i-type parameter is not a string type.
I've written a short code to download and rename files from a specific folder in my outlook account. The code works great, the only problem is that I typically need to run the code several times to actually download all of the messages. It seems the code is just failing to acknowledge some of the messages, there are no errors when I run through it.
I've tried a few things like walking through each line step by step in the python window, running the code with outlook closed or opened, and trying to print the files after they're successfully saved to see if there are specific messages that are causing the problem.
Here's my code
#! python3
# downloadAttachments.py - Downloads all of the weight tickets from Bucky
# Currently saves to desktop due to instability of I: drive connection
import win32com.client, os, re
#This line opens the outlook application
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
#Not exactly sure why the inbox is default folder 6 but it works
inbox = outlook.GetDefaultFolder(6)
#box where the messages are to save
TicketSave = inbox.Folders('WDE').Folders('SAVE').Folders('TicketSave')
#box where the messages are moved to
done = inbox.Folders('WDE').Folders('CHES').Folders('Weight Tickets')
ticketMessages = TicketSave.Items
#Key is used to verify the subject line is correct. This script only works if the person sends
# their emails with a consistent subject line (can be altered for other cases)
key = re.compile(r'wde load \d{3}') #requires regulars expressions (i.e. 'import re')
for message in ticketMessages:
#will skip any message that does not match the correct subject line format (non-case sensitive)
check = str(message.Subject).lower()
if key.search(check) == None:
continue
attachments = message.Attachments
tic = attachments.item(1)
ticnum = str(message.Subject).split()[2]
name = str(tic).split()[0] + ' ticket ' + ticnum + '.pdf' #changes the filename
tic.SaveAsFile('C:\\Users\\bhalvorson\\Desktop\\Attachments' + os.sep + str(name))
if message.UnRead == True:
message.UnRead = False
message.Move(done)
print('Ticket pdf: ' + name + ' save successfully')
Alright I found the answer to my own question. I'll post it here in case any other youngster runs into the same problem as me.
The main problem is the "message.Move(done)" second from the bottom.
Apparently the move function alters the current folder thus altering the number of loops that the for loop will go through. So, the way it's written above, the code only ever processes half of the items in the folder.
An easy work around is to switch the main line of the for loop to "for message in list(ticketMessages):" the list is not affected by the Move function and therefore you'll be able to loop through every message.
Hope this helps someone.
The follwowing is a code written in blender python
import bpy
import os
import subprocess
import time
bpy.data.materials["Material"].use_face_texture = True
customer_id = "john33"
image1 = "orangeskin.jpg" # define image 1 from folder
imagepath1 = "C:\\Users\\mrryan\\Desktop\\models\\materialsjpeg\\"
image2 = "elec.jpg"
imagepath2 = "C:\\Users\\mrryan\\Desktop\\models\\materialsjpeg\\"
#Step 1 go to Edit Mode
bpy.ops.object.editmode_toggle()
#step 2 insert image in the Image Editor
bpy.context.area.type = 'IMAGE_EDITOR'
bpy.ops.uv.smart_project()
bpy.ops.image.open(filepath = image1, directory= imagepath1 ,
files=[{"name":image1, "name":image1}])
#Step 3 back to Object Mode TRY AND CHANGE TITLE TO `CUSTOMER_ID AND FILE`
bpy.ops.object.editmode_toggle()
bpy.data.window_managers["WinMan"].sketchfab.title = "orangetube"
#Step 4 save new.blend
filepath = os.path.join(r"C:\Users\mrryan\Desktop", customer_id + "orangytube.blend")
bpy.ops.wm.save_as_mainfile(filepath = filepath)
toggleedit = bpy.ops.object.editmode_toggle()
#here is the problem!!!!
subprocess.call(["bpy.ops.export.sketchfab()"], shell = True)
#new texture
#Step 1 go to Edit Mode
#step 2 insert image in the Image Editor
bpy.context.area.type = 'IMAGE_EDITOR'
bpy.ops.image.open(filepath= image2, directory= imagepath2,
files= [{"name":image2,"name":image2}])
bpy.ops.uv.smart_project()
#Step 3 back to Object Mode
bpy.ops.object.editmode_toggle()
bpy.data.window_managers["WinMan"].sketchfab.title = "elec-tube"
#Step 4 save new.blend
filepath = os.path.join(r"C:\Users\mrryan\Desktop", customer_id + "elec-tube.blend")
bpy.ops.wm.save_as_mainfile(filepath = filepath)
bpy.ops.export.sketchfab()
The idea is, eventually to programmatically change the active object( which is combined but not joined with another object) to have different materials and change the shape, etc. Each individual model combination would be uploaded to sketchfab separately and saved as a blender file.
At this stage my problem is that, while uploading, the script does not wait until the first upload is finished.This generates an error that says to please wait for the current upload to finish. and as a result only one model is uploaded.
subprocess.Popen
has been tried but i could not get it to work.
Perhaps the return values were wrong?
How can you get the script to upload the first model, wait until finished, then continue to make the adjustments and upload the second?
(I work at Sketchfab, but I don't have much experience in Blender scripting).
The issue is that the Sketchfab Blender Exporter uses a thread for the upload, meaning the subprocess will return, with a thread still running in the background.
I would experiment in launching bpy.ops.export.sketchfab() using a new Thread and join(), instead of just subprocess.
The following resources might help:
python multithreading wait till all threads finished
https://docs.python.org/2/library/multiprocessing.html
http://www.blender.org/documentation/blender_python_api_2_62_2/info_gotcha.html#strange-errors-using-threading-module
Let us know how it goes!
We have an AJA Ki Pro recorder at another location and I a need to created an automated system that pulls the recorded files over to my editing studio. So far I have successfully been able to pull recordings using a python script run via an Applescript through Automator. I than can trigger the application from iCal. Basically my script involves setting the "MediaState" parameter on my recorder to "Data" (value=1) so I can pull files, comparing the files on the recorder to my local files (it only downloads what I dont already have locally), and then setting the "MediaState" property back to "Rec" (value=0) so the recorder is ready to go again.
Here are the 2 problems I have been unable to resolve so far. Bear with me, I have about 2 days worth of experience with Python :) It seems that I have somehow created a loop where it constantly says "Looking for new clips" and "No new clips found". Ideally I would like to have the script terminate if no new clips are found. I would also like to set this up so that when it finishes a download through cURL, it automatically sets my "MediaState" back to value=0 and ends the script. Here is my code so far:
# This script polls the unit downloads any new clips it hasn't already downloaded to the current directory
# Arguments: hostname or IP address of Ki Pro unit
import urllib, sys, string, os, posix, time
def is_download_allowed(address):
f = urllib.urlopen("http://"+address+"/config?action=get¶mid=eParamID_MediaState")
response = f.read()
if (response.find('"value":"1"') > -1):
return True
f = urllib.urlopen("http://"+address+"/config?action=set¶mid=eParamID_MediaState&value=1")
def download_clip(clip):
url = "http://" + address + "/media/" + clip
print url
posix.system("curl --output " + clip + " " + url);
def download_clips(response):
values = response.split(":")
i = 0
for word in values:
i += 1
if(word.find('clipname') > -1):
clip = values[i].split(',')[0].translate(string.maketrans("",""), '[]{} \,\"\" ')
if not os.path.exists(clip):
print "Downloading clip: " + clip
download_clip(clip)
else:
f = urllib.urlopen("http://"+address+"/config?action=set¶mid=eParamID_MediaState&value=0")
print "No new clips found"
address = sys.argv[1]
while 1:
if (is_download_allowed(address)):
print "Looking for new clips"
f = urllib.urlopen("http://"+address+"/clips")
response = f.read()
download_clips(response)
If the download_clips function is looping through the clip names, why you need that infinite while 1 loop? I think it is not necessary. Just remove it and dedent the block.