cut parts of a video using gstreamer/Python (gnonlin?) - python

I have a video file and I'd like to cut out some scenes (either identified by a time position or a frame). As far as I understand that should be possible with gnonlin but so far I wasn't able to find a sample how to that (ideally using Python). I don't want to modify the video/audio parts if possible (but conversion to mp4/webm would be acceptable).
Am I correct that gnonlin is the right component in the gstreamer universe to do that? Also I'd be glad for some pointers/recipes how to approach the problem (gstreamer newbie).

Actually it turns out that "gnonlin" is too low-level and still requires a lot of gstreamer knowledge. Luckily there is "gstreamer-editing-services" (gst-editing-services) which is a
library offering a higher level API on top of gstreamer and gnonlin.
With a tiny bit of RTFM reading and a helpful blog post with a Python example I was able to solve my basic problem:
Load the asset (video)
Create a Timeline with a single layer
add the asset multiple times to the layer, adjusting start, inpoint and duration so only the relevant parts of a video are present in the output video
Most of my code is directly taken from the referenced blog post above so I don't want to dump all of that here. The relevant stuff is this:
asset = GES.UriClipAsset.request_sync(source_uri)
timeline = GES.Timeline.new_audio_video()
layer = timeline.append_layer()
start_on_timeline = 0
start_position_asset = 10 * 60 * Gst.SECOND
duration = 5 * Gst.SECOND
# GES.TrackType.UNKNOWN => add every kind of stream to the timeline
clip = layer.add_asset(asset, start_on_timeline, start_position_asset,
duration, GES.TrackType.UNKNOWN)
start_on_timeline = duration
start_position_asset = start_position_asset + 60 * Gst.SECOND
duration = 20 * Gst.SECOND
clip2 = layer.add_asset(asset, start_on_timeline, start_position_asset,
duration, GES.TrackType.UNKNOWN)
timeline.commit()
The resulting video includes the segments 10:00–10:05 and 11:05-11:25 so essentially there are two cuts: One in the beginning and one in the middle.
From what I have seen this worked perfectly fine, audio and video in sync, no worries about key frames and whatnot. The only part left is to find out if I can translate the "frame number" into a timing reference for gst editing services.

Related

python webrtc voice activity detection is wrong

I need to do voice activity detection as a step to classify audio files.
Basically, I need to know with certainty if a given audio has spoken language.
I am using py-webrtcvad, which I found in git-hub and is scarcely documented:
https://github.com/wiseman/py-webrtcvad
Thing is, when I try it on my own audio files, it works fine with the ones that have speech but keeps yielding false positives when I feed it with other types of audio (like music or bird sound), even if I set aggressiveness at 3.
Audios are 8000 sample/hz
The only thing I changed to the source code was the way I pass the arguments to main function (excluding sys.args).
def main(file, agresividad):
audio, sample_rate = read_wave(file)
vad = webrtcvad.Vad(int(agresividad))
frames = frame_generator(30, audio, sample_rate)
frames = list(frames)
segments = vad_collector(sample_rate, 30, 300, vad, frames)
for i, segment in enumerate(segments):
path = 'chunk-%002d.wav' % (i,)
print(' Writing %s' % (path,))
write_wave(path, segment, sample_rate)
if __name__ == '__main__':
file = 'myfilename.wav'
agresividad = 3 #aggressiveness
main(file, agresividad)
I'm seeing the same thing. I'm afraid that's just the extent to which it works. Speech detection is a difficult task and webrtcvad wants to be light on resources so there's only so much you can do. If you need more accuracy then you would need different packages/methods that will necessarily take more computing power.
On aggressiveness, you're right that even on 3 there are still a lot of false positives. I'm also seeing false negatives however so one trick I'm using is running three instances of the detector, one for each aggressiveness setting. Then instead of classifying a frame 0 or 1 I give it the value of the highest aggressiveness that still said it was speech. In other words each sample now has a score of 0 to 3 with 0 meaning even the least strict detector said it wasn't speech and 3 meaning even the strictest setting said it was. I get a little bit more resolution like that and even with the false positives it is good enough for me.

Using Steam WebAPI to get total time playing a game

I'm trying to get access to the amount of TF2 time played using the Steam API. I'm currently using:-
http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=440&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&steamid=xxxxxxxxxxxxxxxxx&format=xml
And then filter through the XML and extracting the time played relating to each of the classes (e.g. pyro (Pyro.accum.iPlayTime), etc). This worked ok but I think missing the MVM classes made my final value incorrect (my code, in Python, returned 977 when online sites say over 1600 hours). Adding the MVM classes (plus possibly others) may provide th ecorrect result but it's making the code very long winded.
So I was wondering if there is a call in the Steam Web API that will just give me the total time played without having to go though all the extracting and adding?
I have looked through the Steam Web API Developer page, but can't find any reference to what I'm after.
Added Code:
if __name__ == '__main__':
import urllib2
import xml.etree.ElementTree as ET
import datetime
timeKISA = 0
playerStatsKISA = urllib2.urlopen('http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=440&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&steamid=xxxxxxxxxxxxxxxxx&format=xml')
statsKISA = playerStatsKISA.read()
theStatsKISA = ET.fromstring(statsKISA)
for stat in theStatsKISA.findall("./stats/stat"):
if stat.find('name').text.startswith('Scout.accum.iPlayTime') or \
stat.find('name').text.startswith('Soldier.accum.iPlayTime') or \
stat.find('name').text.startswith('Engineer.accum.iPlayTime') or \
stat.find('name').text.startswith('Medic.accum.iPlayTime') or \
stat.find('name').text.startswith('Spy.accum.iPlayTime') or \
stat.find('name').text.startswith('Sniper.accum.iPlayTime') or \
stat.find('name').text.startswith('Demoman.accum.iPlayTime') or \
stat.find('name').text.startswith('Heavy.accum.iPlayTime') or \
stat.find('name').text.startswith('Pyro.accum.iPlayTime'):
timeKISA = timeKISA + int(stat.find('value').text)
finalTimeKISA = timeKISA / 60 / 60
KISATime = ('KISA_Time=' + str(finalTimeKISA) + ' hours')
print KISATime
Thank you.
Markus
Pulling my comments into an answer,
It is my understanding that the *.accum.iPlayTime fields are cumulative for your place time as that class regardless of game mode or map. Based on my own stats (and a glance at a few others on my friend list), this matches exactly what Steam Community reports that play time is. Additionally, it is reporting that your playtime matches these fields on your TF2 Achievements page.
A couple notes:
The summary page on a player's profile does not seem to match the actual stats that the achievements page shows. I'm unsure if this is a Steam Community issue or a summary of additional fields. However, the achievements page, which has detailed break downs of play time by class, longest life, etc. is using the same data as the GetUserStatsForGame API call.
You have a very minor formating issue in your code. The very last print KISATime is indented one to many times and therefore prints the KISA_Time = line multiple times. If you pull it out of the for loop, you will only get the print line once.
If you change your finalTimeKISA = timeKISA / 60 / 60 to be decimal 60.0 you will get the decimal answers. Otherwise, as is, you will only receive an integer answer.

Why are my Datastore Write Ops so high?

I busted through my daily free quota on a new project this weekend. For reference, that's .05 million writes, or 50,000 if my math is right.
Below is the only code in my project that is making any Datastore write operations.
old = Streams.query().fetch(keys_only=True)
ndb.delete_multi(old)
try:
r = urlfetch.fetch(url=streams_url,
method=urlfetch.GET)
streams = json.loads(r.content)
for stream in streams['streams']:
stream = Streams(channel_id=stream['_id'],
display_name=stream['channel']['display_name'],
name=stream['channel']['name'],
game=stream['channel']['game'],
status=stream['channel']['status'],
delay_timer=stream['channel']['delay'],
channel_url=stream['channel']['url'],
viewers=stream['viewers'],
logo=stream['channel']['logo'],
background=stream['channel']['background'],
video_banner=stream['channel']['video_banner'],
preview_medium=stream['preview']['medium'],
preview_large=stream['preview']['large'],
videos_url=stream['channel']['_links']['videos'],
chat_url=stream['channel']['_links']['chat'])
stream.put()
self.response.out.write("Done")
except urlfetch.Error, e:
self.response.out.write(e)
This is what I know:
There will never be more than 25 "stream" in "streams." It's
guaranteed to call .put() exactly 25 times.
I delete everything from the table at the start of this call because everything needs to be refreshed every time it runs.
Right now, this code is on a cron running every 60 seconds. It will never run more often than once a minute.
I have verified all of this by enabling Appstats and I can see the datastore_v3.Put count go up by 25 every minute, as intended.
I have to be doing something wrong here, because 25 a minute is 1,500 writes an hour, not the ~50,000 that I'm seeing now.
Thanks
You are mixing two different things here: write API calls (what your code calls) and low-level datastore write operations. See the billing docs for relations: Pricing of Costs for Datastore Calls (second section).
This is the relevant part:
New Entity Put (per entity, regardless of entity size) = 2 writes + 2 writes per indexed property value + 1 write per composite index value
In your case Streams has 15 indexed properties resulting in: 2 + 15 * 2 = 32 write OPs per write API call.
Total per hour: 60 (requests/hour) * 25 (puts/request) * 32 (operations/put) = 48,000 datastore write operations per hour
It seems as though I've finally figured out what was going on, so I wanted to update here.
I found this older answer: https://stackoverflow.com/a/17079348/1452497.
I've missed somewhere along the line where the properties being indexed were somehow multiplying the writes by factors of at least 10, I did not expect that. I didn't need everything indexed and after turning off the index in my model, I've noticed the write ops drop DRAMATICALLY. Down to about where I expect them.
Thanks guys!
It is 1500*24=36,000 writes/day, which is very near to the daily quota.

How do I read a midi file, change its instrument, and write it back?

I want to parse an already existing .mid file, change its instrument, from 'acoustic grand piano' to 'violin' for example, and save it back or as another .mid file.
From what I saw in the documentation, the instrument gets altered with a program_change or patch_change directive but I cannot find any library that does this in MIDI files that exist already. They all seem to support it only MIDI files created from scratch.
The MIDI package will do this for you, but the exact approach depends on the original contents of the midi file.
A midi file consists of one or more tracks, and each track is a sequence of events on any of sixteen channels, such as Note Off, Note On, Program Change etc. The last of these will change the instrument assigned to a channel, and that is what you need to change or add.
Without any Program Change events at all, a channel will use program number (voice number) zero, which is an acoustic grand piano. If you want to change the instrument for such a channel then all you need to do is add a new Program Change event for this channel at the beginning of the track.
However if a channel already has a Program Change event then adding a new one at the beginning will have no effect because it is immediately overridden by the pre-existing one. In this case you will have to change the parameters of the existing event to use the instrument that you want.
Things could be even more complicated if there are originally several Program Change events for a channel, meaning that the instrument changes throughout the track. This is unusual, but if you come across a file like this you will have to decide how you want to change it.
Supposing you have a very simple midi file with a single track, one channel, and no existing Program Change events. This program creates a new MIDI::Opus object from the file, accesses the list of tracks (with only a single member), and takes a reference to the list of the first track's events. Then a new Program Change event (this module calls it patch_change) for channel 0 is unshifted onto the beginning of the event list. The new event has a program number of 40 - violin - so this channel will now be played with a violin instead of a piano.
With multiple tracks, multiple channels, and existing Program Change events the task becomes more complex, but the principle is the same - decide what needs to be done and alter the list of events as necessary.
use strict;
use warnings;
use MIDI;
my $opus = MIDI::Opus->new( { from_file => 'song.mid' } );
my $tracks = $opus->tracks_r;
my $track0_events = $tracks->[0]->events_r;
unshift #$track0_events, ['patch_change', 0, 0, 40];
$opus->write_to_file('newsong.mid');
Use the music21 library (plugging my own system, hope that's okay). If there are patches defined in the parts, do:
from music21 import converter,instrument # or import *
s = converter.parse('/Users/cuthbert/Desktop/oldfilename.mid')
for el in s.recurse():
if 'Instrument' in el.classes: # or 'Piano'
el.activeSite.replace(el, instrument.Violin())
s.write('midi', '/Users/cuthbert/Desktop/newfilename.mid')
or if there are no patch changes currently defined:
from music21 import converter,instrument # or import *
s = converter.parse('/Users/cuthbert/Desktop/oldfilename.mid')
for p in s.parts:
p.insert(0, instrument.Violin())
s.write('midi', '/Users/cuthbert/Desktop/newfilename.mid')

What's the best performing xml parsing for GAE (Python Version)?

I think we all know this page, but the benchmarks provided dated from more than two years ago. So, I would like to know if you could point out the best xml parser around. As I need just a xml parser, the more important thing to me is speed over everything else.
My objective is to process some xml feeds (about 25k) that are 4kb in size (this will be a daily task). As you probably know, I'm restricted by the 30 seconds request timeout. So, what's the best parser (Python only) that I can use?
Thanks for your anwsers.
Edit 01:
#Peter Recore
I'll. I'm writing some code now and plan to run some profiling in the near future. Regarding your question, the answer is no. Processing takes just a little time when compared with downloading the actual xml feed. But, I can't increase Google's Bandwidth, so I can only focus on this right now.
My only problem is that i need to do this as fastest as possible because my objective is to get a snapshot of a website status. And, as internet is live and people keep adding and changing it's data, i need the fastest method because any data insertion during the "downloading and processing" time span will actually mess with my statistical analisys.
I used to do it from my own computer and the process took 24 minutes back then, but now the website has 12 times more information.
I know that this don't awnser my question directly, but id does what i just needed.
I remenbered that xml is not the only file type I could use, so instead of using a xml parser I choose to use json. About 2.5 times smaller in size. What means a decrease in download time. I used simplejson as my json libray.
I used from google.appengine.api import urlfetch to get the json feeds in parallel:
class GetEntityJSON(webapp.RequestHandler):
def post(self):
url = 'http://url.that.generates.the.feeds/'
if self.request.get('idList'):
idList = self.request.get('idList').split(',')
try:
asyncRequests = self._asyncFetch([url + id + '.json' for id in idList])
except urlfetch.DownloadError:
# Dealed with time out errors (#5) as these were very frequent
for result in asyncRequests:
if result.status_code == 200:
entityJSON = simplejson.loads(result.content)
# Filled a database entity with some json info. It goes like this:
# entity= Entity(
# name = entityJSON['name'],
# dateOfBirth = entityJSON['date_of_birth']
# ).put()
self.redirect('/')
def _asyncFetch(self, urlList):
rpcs = []
for url in urlList:
rpc = urlfetch.create_rpc(deadline = 10)
urlfetch.make_fetch_call(rpc, url)
rpcs.append(rpc)
return [rpc.get_result() for rpc in rpcs]
I tried getting 10 feeds at a time, but most of the times an individual feed raised the DownloadError #5 (Time out). Then, I increased the deadline to 10 seconds and started getting 5 feeds at a time.
But still, 25k feeds getting 5 at a time results in 5k calls. In a queue that can spawn 5 tasks a second, the total task time should be 17min in the end.

Categories