How to add additional audio track to video player with libvlc - python

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.

Related

VLC Python Binding only playing the first mp4 file in the folder

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.

Examining file on different node (different IP address), same network - possible?

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

python-vlc find current playing audio

I want know to which audio is currently playing in my playlist.
eg: it's path, title or index position
import vlc
instance = vlc.Instance()
player = instance.media_list_player_new()
player.set_playback_mode(vlc.PlaybackMode.loop)
songs = ['music1.mp3', 'music2.mp3', 'music.mp3']
media_list = instance.media_list_new(songs)
player.set_media_list(media_list)
player.play_item_at_index(1)
I tried bunch of methods unfortunately none of them works.
I found the solution for my problem.
Actually the instance is inherited from the get_media_player && get_media using this you can find current playing song base path.
import vlc
instance = vlc.Instance()
player = instance.media_list_player_new()
player.set_playback_mode(vlc.PlaybackMode.loop)
songs = ['music1.mp3', 'music2.mp3', 'music.mp3']
media_list = instance.media_list_new(songs)
player.set_media_list(media_list)
player.play_item_at_index(1)
player.get_media_player().get_media().get_mrl() # will return path of current playing mp3 or mp4

How to incorporate SSML into Python

I need to use SSML to play an audio file with the tag in my Alexa Skill (as per Amazon's instructions).
Problem is, I don't know how to use SSML with Python. I know I can use it with Java but I want to build my skills with Python. I've looked all over, but haven't found any working examples of SSML in a Python script/program - does anyone know?
This was asked two years ago but maybe someone will benefit from the below.
I've just checked and if you use Alexa Skills Kit SDK for Python you can simply add SSML to your response, for example:
#sb.request_handler(can_handle_func=is_request_type("LaunchRequest"))
def launch_request_handler(handler_input):
speech_text = "Wait for it 3 seconds<break time="3s"/> Buuuu!"
return handler_input.response_builder.speak(speech_text).response
Hope this helps.
SSML audio resides in the response.outputSpeech.ssml attribute. Here is an
example obj with other required parameters removed:
{
"response": {
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>
Welcome to Car-Fu.
<audio src="https://carfu.com/audio/carfu-welcome.mp3" />
You can order a ride, or request a fare estimate. Which will it be?
</speak>"
}
}
Further reference:
JSON Interface Reference for Custom Skills
Speech Synthesis Markup Language (SSML) Reference
Install ssml-builder "pip install ssml-builder", and use it:
from ssml_builder.core import Speech
speech = Speech()
speech.add_text('sample text')
ssml = speech.speak()
print(ssml)
These comments really helped a lot in figuring out how to make SSML works using the ask-sdk-python. Instead of
speech_text = "Wait for it 3 seconds<break time="3s"/> Buuuu!" - from wmatt's comment
I defined variables that represents the start and end of every tags that I'm using
ssml_start = '<speak>'
speech_text = ssml_start + whispered_s + "Here are the latest alerts from MMDA" + whispered_e
using single quotes and concatenate those strings to the speech output and it worked! Thanks a lot guys! I appreciate it a lot!
This question was somewhat vague, however I did manage to figure out how to incorporate SSML into a Python script. Here's a snippet that plays some audio:
if 'Item' in intent['slots']:
chosen_item = intent['slots']['Item']['value']
session_attributes = create_attributes(chosen_item)
speech_output = '<speak> Here is something to play' + \
chosen_item + \
'<audio src="https://s3.amazonaws.com/example/example.mp3" /> </speak>'
The ssml package for python exists.
you can install like below by pip
$ pip install pyssml
or
$ pip3 install pyssml
so example is link below
http://blog.naver.com/chandong83/221145083125
sorry. it is korean.
# -*- coding: utf-8 -*-
# for amazon
import re
import os
import sys
import time
from boto3 import client
from botocore.exceptions import BotoCoreError, ClientError
import vlc
from pyssml.PySSML import PySSML
# amazon service fuction
# if isSSML is True, SSML format
# else Text format
def aws_polly(text, isSSML = False):
voiceid = 'Joanna'
try:
polly = client("polly", region_name="ap-northeast-2")
if isSSML:
textType = 'ssml'
else:
textType = 'text'
response = polly.synthesize_speech(
TextType=textType,
Text=text,
OutputFormat="mp3",
VoiceId=voiceid)
# get Audio Stream (mp3 format)
stream = response.get("AudioStream")
# save the audio Stream File
with open('aws_test_tts.mp3', 'wb') as f:
data = stream.read()
f.write(data)
# VLC play audio
# non block
p = vlc.MediaPlayer('./aws_test_tts.mp3')
p.play()
except ( BotoCoreError, ClientError) as err:
print(str(err))
if __name__ == '__main__':
# normal pyssml
#s = PySSML()
# amazon speech ssml
s = AmazonSpeech()
# normal
s.say('i am normal')
# speed is very slow
s.prosody({'rate':"x-slow"}, 'i am very slow')
# volume is very loud
s.prosody({'volume':'x-loud'}, 'my voice is very loud')
# take a one sec
s.pause('1s')
# pitch is very high
s.prosody({'pitch':'x-high'}, 'my tone is very high')
# amazone
s.whisper('i am whispering')
# print to convert to ssml format
print(s.ssml())
# request aws polly and play
aws_polly(s.ssml(), True)
# Wait while playback.
time.sleep(50)

How does vlc.py play video stream?

I want to use vlc.py to play mpeg2 stream http://wiki.videolan.org/Python_bindings.
There are some examples here: http://git.videolan.org/?p=vlc/bindings/python.git;a=tree;f=examples;hb=HEAD
When I run the examples, it just can play video file, I want to know is there any examples to play video stream ?
According to this Pastebin entry, linked to in this mailing list, it can be solved using a method like this:
import vlc
i = vlc.Instance('--verbose 2'.split())
p = i.media_player_new()
p.set_mrl('rtp://#224.1.1.1')
p.play()
I haven't tried it though, so please let me know if it works.
This is a bare bones solution:
import vlc
Instance = vlc.Instance()
player = Instance.media_player_new()
Media = Instance.media_new('http://localhost/postcard/GWPE.avi')
Media.get_mrl()
player.set_media(Media)
player.play()
if the media is a local file you will have to alter:
Media = Instance.media_new('http://localhost/postcard/GWPE.avi')
Media.get_mrl()
to:
Media = Instance.media_new_path('/path/to_your/file/filename.avi')
note that you must lose the get_mrl() as well as changing the function.
import vlc
vlcInstance = vlc.Instance()
player = vlcInstance.media_player_new()
player.set_mrl("rtsp://URL_PATH")
player.play()
I was able to open a stream with the following code, combining the previous answers.
Tested this with a network webcam

Categories

Resources