Skip to content

Instantly share code, notes, and snippets.

@coryalder
Created May 17, 2011 05:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save coryalder/976016 to your computer and use it in GitHub Desktop.
Save coryalder/976016 to your computer and use it in GitHub Desktop.
Creates a glitching effect called "datamoshing" using pygame and pymedia
#! /usr/bin/env python
# based on vplayer.py (http://pymedia.org/tut/src/vplayer.py.html) by dmitry borisov (http://pymedia.org)
# bloated and uglied for fair reasons by sven könig (http://www.popmodernism.org)
import sys, thread, time, traceback, Queue, os
import optparse
import pymedia
import pymedia.video.muxer as muxer
import pymedia.video.vcodec as vcodec
import string
from optparse import OptionParser
from recode_video import RecordVideo
if os.environ.has_key( 'PYCAR_DISPLAY' ) and os.environ[ 'PYCAR_DISPLAY' ]== 'directfb':
import pydfb as pygame
YV12= pygame.PF_YV12
else:
import pygame
YV12= pygame.YV12_OVERLAY
SEEK_SEC= 10
########################################################################3
# Simple video player
class VPlayer:
# ------------------------------------
def __init__(self, playkeyframes=False):
self.frameNum= -1
self.exitFlag= 1
self.ct= None
self.pictureSize= None
self.paused= 0
self.stopPlayback()
self.err= []
self.vBitRate= 0
self.seek= 0
self.seekInProgress = False
self.vc= None
self.frnr = 0
self.lastdelta = None
self.playkeyframes = False
self.loopend = -1
self.loopstart = -1
self.looplen = -1
self.loopremain = -1
self.lastkeyframes = []
self.playkeyframe = playkeyframes
self.nextframetime = 0
self.modifyspeed = 1.
self.nextframenow = False
self.islooping = False
self.paused = False
self.displaynext = True
self.displaychess = False
self.framesavecnt = 0
self.recordvideo = None
def setEventLoop(self, ev):
self.eventloop = ev
def record(self):
if self.recordvideo == None:
print 'Starting recording...'
#~ print self.vc.size
#~ params = self.vc.getParams()
self.recordvideo = RecordVideo()#'record.mpg', None)
#~ print params
def resetVideo( self ):
# Init all used vars first
self.decodeTime= self.vBitRate= self.frameNum= \
self.sndDelay= self.hurry= self.videoPTS= \
self.lastPTS= self.frRate= self.vDelay= 0
self.seek= 0
if self.initADelta!= -1:
self.seekADelta= self.initADelta
# Zeroing out decoded pics queue
self.decodedFrames= []
self.rawFrames= []
try:
if self.vc:
self.vc.reset()
except:
print traceback.print_exc()
def initVideo( self, params ):
self.framerate = params['frame_rate'] / float(params['frame_rate_base'])
# There is no overlay created yet
self.overlay= self.pictureSize= None
try:
# Set the initial sound delay to 0 for now
# It defines initial offset from video in the beginning of the stream
self.initADelta= -1
self.resetVideo()
self.seekADelta= 0
# Setting up the HW video codec
self.vc= pymedia.video.ext_codecs.Decoder( params )
except:
try:
# Fall back to SW video codec
self.vc= vcodec.Decoder( params )
except:
traceback.print_exc()
self.err.append( sys.exc_info()[1] )
def createOverlay( self, vfr ):
# Create overlay if any
self.overlay= pygame.Overlay( YV12, vfr.size )
# Save real picture size
if vfr.aspect_ratio> .0:
self.pictureSize= ( vfr.size[ 1 ]* vfr.aspect_ratio, vfr.size[ 1 ] )
else:
self.pictureSize= vfr.size
# Locate overlay on the screen
self.setOverlay( self.overlayLoc )
def processVideoFrame( self, d, framenr, checkifkf=True, skip=False ):
# See if we should show video frame now
if not self.nextframetime:
self.nextframetime = time.time()
st = self.nextframetime-time.time()
self.nextframetime += 1/(self.framerate*self.modifyspeed)
if st < 0: st = 0
#needs to be done better using another thread
grain = 1/100.
#~ print 'ST>', st
while st > grain:
if self.nextframenow:
self.nextframenow = False
break
time.sleep(grain)
#~ print 'time.sleep(%0.5f)' % grain
st -= grain
else:
time.sleep(st)
#~ print '!!!time.sleep(%0.5f)' % st
if skip:
return
vfr = self.decodeVideoFrame(d, checkifkf)
if vfr:
if self.overlayLoc and self.overlay== None:
self.createOverlay( vfr )
self.overlay.set_data( vfr.data )
#~ print '->', vfr.pict_type
self.overlay.display()
#~ try:
if self.recordvideo:
self.recordvideo.encodeFrame(vfr)
#~ except:
#~ print '$!%&'*40
else:
print 'not vfr'
self.frameshown = vfr
def saveFrame(self):
print 'd'*100
dd= self.frameshown.convert( 2 )
img= pygame.image.fromstring( dd.data, dd.size, "RGB" )
filename = 'savevid/frame_%03d.bmp' % self.framesavecnt
print 'Saving to %s...' % filename
pygame.image.save( img, filename)
self.framesavecnt += 1
def saveFrameReset(self):
self.framesavecnt = 0
def decodeVideoFrame( self, rawframe, checkifkf):
# Decode the video frame
d = rawframe
framenr= 0
rawdata = d[1]
vdstring = rawdata[:10]
iskeyframe = vdstring[:5] == '\0\0\1\0\0' and checkifkf
#~ iskeyframe = vdstring[:4] == '\0\0\1'+chr(176) #xvid
#~ print "schluesselrahmen:", self.frame, '- ', iskeyframe
#~ iskeyframe = False
if iskeyframe:
if self.frame not in self.lastkeyframes:
self.lastkeyframes.append(self.frame)
if len(self.lastkeyframes) > 10:
self.lastkeyframes[:11] = self.lastkeyframes[1:]
#~ print 'LASTKEYFRAMES:', self.lastkeyframes
vddec = '%04i: ' % self.frnr
for char in vdstring:
vddec += '%i-' % ord(char)
#~ print vddec[:-1]
#~ print vdstring
if not self.playkeyframes:
if not self.seek:
if self.frnr == 0:
rawdata = d[1]
elif not iskeyframe:
rawdata = d[1]
self.lastdelta = rawdata
else:
rawdata = self.lastdelta
else:
rawdata = d[1]
else:
rawdata = d[1]
try:
vfr= self.vc.decode(rawdata)
except:
print traceback.print_exc()
vfr = None
self.frnr += 1
return vfr
def start( self ):
if self.ct:
raise 'cannot run another copy of vplayer'
self.exitFlag= 0
self.ct= thread.start_new_thread( self.readerLoop, () )
def stop( self ):
# Stop if anything is playing now
self.stopPlayback()
# Turn the flag to exist the main thread
self.exitFlag= 1
def pause(self):
self.paused = not self.paused
self.displaynext = not self.paused
def displayChess(self):
self.displaychess = True
self.displaynext = True
def startPlayback( self, file ):
# Stop if anything is playing now
self.stopPlayback()
# Set the new file for playing
self.playingFile= file
def stopPlayback( self, bForce= True ):
# Close the overlay
self.setOverlay( None )
self.playingFile= None
# Unpause playback if any
self.paused= 0
def jumpTime( self, secs ):
frames = int(secs * self.framerate)
self.jumpFrames(frames)
def jumpFrames( self, frames ):
self.frame += frames
self.displaynext = True
if self.loopend != -1:
self.loopend += frames
self.loopstart += frames
def loopHold(self, secs):
#~ print '+++'
self.looplen_f = length = float(secs) * self.framerate
self.looplen_r = 0
#~ print 'self.looplen_f:', length
#~ self.looplen_i = int(self.looplen_f+0.5)
#~ print 'self.looplen_i:', self.looplen_i
#~ self.loopremain = self.looplen_f - self.looplen_i
#~ print 'loopremain:', self.loopremain
self._llast = None
self._lcnt = 0
self._lall = 0
if not self.islooping:
self.loopstart = self.frame
else:
self.frame = self.loopstart
self.islooping = True
self.setLoopEnd()
def setLoopEnd(self):
if self._lcnt:
ltime = time.time() - self._llast
#~ print 'LTIME>', ltime
else:
ltime = 0
self._llast = time.time()
self._lall += ltime
if self._lcnt:
pass
#~ print 'AVRG:', self._lall / self._lcnt
#~ print 'self._lcnt:', self._lcnt
#~ print 'self._llast:', self._llast
#~ print 'self._lall:', self._lall
#~ print '-'*40
self._lcnt += 1
#~ print 'self.looplen_f:', self.looplen_f
looplen_i = int(self.looplen_f + self.looplen_r + 0.5)
#~ print 'looplen_i:', looplen_i
self.looplen_r += self.looplen_f - looplen_i
#~ print 'loopremain:', self.looplen_r
#~ print 'looplen_i:', looplen_i
self.loopend = self.loopstart + looplen_i
#~ print '\n -+- \n'
def jumpToLoopStart(self):
if self.islooping:
self.looplen_r = 0
self.frame = self.loopstart
def jumpToFrame(self, frame):
self.frame = frame
def playKeyFrame(self, kfnr):
print 'PLAYRRR', kfnr
if not len(self.lastkeyframes):
return
if kfnr >= len(self.lastkeyframes):
kfnr = len(self.lastkeyframes)-1
self.playkeyframe = self.lastkeyframes[kfnr]
self.displaynext = True
def loopRelease(self):
self.loopend = -1
self.loopstart = -1
self.islooping = False
def setOverlay( self, loc ):
self.overlayLoc= loc
print "self.overlayLoc= ", loc
if loc== None:
self.overlay= None
elif self.overlay:
# Calc the scaling factor
sw,sh= self.overlayLoc[ 2: ]
w,h= self.pictureSize
x,y= self.overlayLoc[ :2 ]
factor= min( float(sw)/float(w), float(sh)/float(h) )
# Find appropriate x or y pos
x= ( sw- factor* w ) / 2+ x
y= ( sh- factor* h ) / 2+ y
self.overlay.set_location( (int(x),int(y),int(float(w)*factor),int(float(h)*factor)) )
def isPlaying( self ):
return self.overlay!= None
def openVideo(self, file):
format = file.split( '.' )[ -1 ].lower()
dm= muxer.Demuxer( format )
f= open(file, 'rb')
rawdata = f.read()
rawstream = dm.parse(rawdata)
return dm, f, rawdata, rawstream
def readerLoop( self ):
"""
"""
print 'Main video loop has started.'
f= None
try:
while self.exitFlag== 0:
if self.playingFile== None:
time.sleep( 0.01 )
continue
self.frameNum= -1
if 1: # open chess !DIRTYHACK!
dm, f, s, rawstream = self.openVideo('chess/chess.avi')
print 'STREAMS:', dm.streams
# Setup video( only first matching stream will be used )
self.err= []
for vindex in xrange( len( dm.streams )):
if dm.streams[ vindex ][ 'type' ]== muxer.CODEC_TYPE_VIDEO:
self.initVideo( dm.streams[ vindex ] )
break
i=0
while i < len(rawstream):
if rawstream[i][0] != muxer.CODEC_TYPE_VIDEO:
del rawstream[i]
else:
i+= 1
self.chess = rawstream[0]
# Initialize demuxer and read small portion of the file to have more info on the format
dm, f, s, rawstream = self.openVideo(self.playingFile['name'])
print 'STREAMS:', dm.streams
# Setup video( only first matching stream will be used )
self.err= []
for vindex in xrange( len( dm.streams )):
if dm.streams[ vindex ][ 'type' ]== muxer.CODEC_TYPE_VIDEO:
self.initVideo( dm.streams[ vindex ] )
break
i=0
while i < len(rawstream):
if rawstream[i][0] != muxer.CODEC_TYPE_VIDEO:
del rawstream[i]
else:
i+= 1
self.numframes = len(rawstream)
# Play until no exit flag, not eof, no errs
while len(s) and len( self.err )== 0 and self.exitFlag== 0 and self.playingFile:
self.frame = 0
while 1:
#~ print 'frame:', self.frame
if self.frame == self.numframes - 1:
time.sleep(0.01)
continue
if self.paused and not self.displaynext:
self.processVideoFrame( None, None, None, skip=True )
continue
if self.displaychess:
self.processVideoFrame( self.chess, self.frame, False )
self.displaychess = False
self.displaynext = False
continue
if self.playkeyframe is None:
d = rawstream[self.frame]
self.processVideoFrame( d, self.frame )
else:
print 'x'*30, self.playkeyframe
d = rawstream[self.playkeyframe]
self.playkeyframe = None
self.processVideoFrame( d, self.frame, False )
if self.frame == self.loopend:
self.frame = self.loopstart
self.setLoopEnd()
if self.paused:
self.displaynext = False
continue
self.frame += 1
print 'ente.'
except:
print traceback.print_exc()
try:
self.stopPlayback()
except:
print traceback.print_exc()
print 'Main video loop has closed.'
print 'QUARK!'
def setModifySpeed(self, speed):
self.modifyspeed = speed
#~ print 'mod:', speed
class Display:
def __init__(self, fullscreen, player):
self.player = player
pygame.init()
self._fullscreen = fullscreen
self.setDisplay(fullscreen)
def setDisplay(self, fullscreen):
if fullscreen:
res = 1024, 768
opts = pygame.FULLSCREEN | pygame.HWSURFACE
else:
res = 720, 528
opts = 0
pygame.display.set_mode(res, opts)
#~ self.player.setOverlay( (0,0,res[0],res[1]) )
def switchFullScreen(self):
self._fullscreen = not self._fullscreen
self.setDisplay(self._fullscreen)
EVT_KEYREPEAT = 31
class EventLoop:
def __init__(self, display):
self.display = display
p = pygame
self.keydown = []
self.keyup = []
self.mousedown = []
self.keydown.append(((p.K_ESCAPE,), self.exit))
self.keydown.append(((p.K_a, p.K_s, p.K_d, p.K_f, p.K_j, p.K_k, p.K_l, 59), self.jumpTime))
self.keydown.append(((p.K_g, p.K_h), self.jumpFrames))
self.keydown.append(((p.K_1, p.K_2, p.K_3, p.K_4, p.K_5, p.K_6, p.K_7, p.K_8, p.K_9), self.loopPlayer))
self.keydown.append(((p.K_z, p.K_x, p.K_c, p.K_v, p.K_b, p.K_n, p.K_m, 44, 46, 47), self.playKeyFrame))
self.keydown.append(((p.K_0,), self.releasePlayer))
#~ self.keydown.append(((p.K_F11,), self.switchFullScreen))
self.keydown.append(((p.K_SPACE,), self.tapTempo))
self.keydown.append(((p.K_PAUSE,), self.pause))
#~ self.keydown.append(((p.K_PAGEUP, p.K_PAGEUP), self.frame))
#~ self.keydown.append(((p.K_RETURN,), self.displayChess))
#~ self.keydown.append(((p.K_RETURN,), self.jumpToLoopStart))
self.keydown.append(((p.K_RETURN,), self.jumpToFrame_0))
self.keydown.append(((92,), self.jumpToFrame_1))
self.keydown.append(((p.K_F12,), self.saveFrame))
self.keydown.append(((p.K_F11,), self.saveFrameReset))
self.keydown.append(((p.K_r,), self.record))
self.keydown.append(((p.K_LEFT,), self.slowDownDown))
self.keyup.append (((p.K_LEFT,), self.slowDownUp))
self.keydown.append(((p.K_RIGHT,), self.speedUpDown))
self.keyup.append (((p.K_RIGHT,), self.speedUpUp))
self.leftisdown = False
self.rightisdown = False
self.mousedown = []
self.mousedown.append(((4,), self.frameForward))
self.mousedown.append(((5,), self.frameBackward))
self.modifyspeed = 1.
pygame.time.set_timer(EVT_KEYREPEAT, 10)
self.keyrepeatfuncs = []
self._exit = False
self._fullscreen = False
self.lasttaps = None
p = pygame
bpm = 193.71881420
fourth = 60/bpm
sth = fourth/4
self._jumptime = {p.K_a: -fourth*4, p.K_s: -fourth*1,
p.K_d: -fourth/2, p.K_f: -fourth/4,
p.K_j: fourth/4, p.K_k: fourth/2,
p.K_l: fourth*1, 59: fourth*4 } #59 -> 'oe'
self._jumpframe = {p.K_g: -1, p.K_h: 1}
self._looptime = {p.K_1: sth*1, p.K_2: sth*2,
p.K_3: sth*3, p.K_4: sth*4,
p.K_5: sth*5, p.K_6: sth*6,
p.K_7: sth*7, p.K_8: sth*8,
p.K_9: sth*9 }
self._keyframe = {p.K_z: 0, p.K_x: 1,
p.K_c: 2, p.K_v: 3,
p.K_b: 4, p.K_n: 5,
p.K_m: 6, 44: 7,
46: 8, 47: 9}
def record(self, *key):
self.player.record()
def saveFrame(self, *key):
self.player.saveFrame()
def saveFrameReset(self, *key):
self.player.saveFrameReset()
def pause(self, *key):
self.player.pause()
def displayChess(self, *key):
self.player.displayChess()
def start(self, player):
self.player = player
while player.isPlaying()== 0:
time.sleep( .05 )
while player.isPlaying():
e= pygame.event.wait()
#~ print "EVENT:", e.type
if e.type == pygame.KEYDOWN:
#~ print 'keydown:', e.key
for keygroup in self.keydown:
if e.key in keygroup[0]:
keygroup[1](e.key)
if e.type == pygame.KEYUP:
for keygroup in self.keyup:
if e.key in keygroup[0]:
keygroup[1](e.key)
elif e.type == pygame.MOUSEBUTTONDOWN:
for btngroup in self.mousedown:
if e.button in btngroup[0]:
btngroup[1](e.button)
elif e.type == EVT_KEYREPEAT:
for func in self.keyrepeatfuncs:
func()
elif e.type == pygame.QUIT:
self.exit()
if self._exit:
break
def exit(self, key=None):
self.player.stopPlayback()
self._exit = True
def quit(self):
self._exit = True
def jumpTime(self, key):
self.player.jumpTime(self._jumptime[key])
def jumpFrames(self, key):
self.player.jumpFrames(self._jumpframe[key])
def jumpToLoopStart(self, key):
self.player.jumpToLoopStart()
def jumpToFrame_0(self, key):
self.player.jumpToFrame(271)
def jumpToFrame_1(self, key):
self.player.jumpToFrame(4004)
def frameForward(self, *args):
self.player.jumpFrames(1)
def frameBackward(self, *args):
self.player.jumpFrames(-1)
def loopPlayer(self, key):
self.player.loopHold(self._looptime[key])
def playKeyFrame(self, key):
self.player.playKeyFrame(self._keyframe[key])
def releasePlayer(self, key):
self.player.loopRelease()
def slowDownDown(self, key):
print 'slowDownDown'
self.leftisdown = True
self.leftright = 'left'
self.modifySpeed()
def slowDownUp(self, key):
print 'slowDownUp'
self.leftisdown = False
self.leftright = 'right'
def speedUpDown(self, key):
print 'speedUpDown'
self.rightisdown = True
self.leftright = 'right'
self.modifySpeed()
def speedUpUp(self, key):
print 'speedUpUp'
self.rightisdown = False
self.leftright = 'left'
def modifySpeed(self):
if not self.leftisdown and not self.rightisdown:
self.player.setModifySpeed(1.)
self.modifyspeed = 1.
if self.modifySpeed in self.keyrepeatfuncs:
self.keyrepeatfuncs.remove(self.modifySpeed)
return
if self.modifySpeed not in self.keyrepeatfuncs:
self.keyrepeatfuncs.append(self.modifySpeed)
if self.leftright == 'left':
self.modifyspeed = max(self.modifyspeed * 0.995, 0.5)
elif self.leftright == 'right':
self.modifyspeed = min(self.modifyspeed * (1/0.995), 2.0)
self.player.setModifySpeed(self.modifyspeed)
def seek(self, key):
pass
def tapTempo(self, key):
if self.lasttaps == None:
self.lasttaps = []
self.lasttaps.append(time.time())
print "TAP! " * 20
print self.lasttaps
print "TAP! " * 20
#~ self.display.switchFullScreen()
def switchFullScreen(self, key):
self.display.switchFullScreen()
########################################################################3
# Simple cache replacer for standalone testing
class Menu:
NAME_KEY= 'name'
class Cache:
def open( self, f ):
return open( f['name'], 'rb' )
def getPathName( self, f ):
return f[ 'name' ]
def getExtension( self, f ):
return f[ 'name' ].split( '.' )[ -1 ].lower()
cache= Cache()
# Menu module instance for standalone testing
menu= Menu()
parser = OptionParser()
parser.add_option("--fullscreen", "-f", action="store_true", dest="fullscreen")
parser.add_option("--keyframes", "-k", action="store_true", dest="keyframes")
print '-'*10
args = sys.argv[1:]
parsed = parser.parse_args(sys.argv[1:])
#~ print '>', parsed[0].fullscreen , '<'
#~ print parsed[1]
#~ sys.exit(0)
videofiles = {}
if (len(sys.argv )< 2 or len( sys.argv )> 3):
print 'Usage: vplayer <file_name|v_id>'
sys.exit(0)
videofile = sys.argv[1]
if videofile[:2] == 'v_':
try:
videofile = videofiles[videofile]
except 'x':
pass
fullscreen = parsed[0].fullscreen
#~ fullscreen = False
#~ playkeyframes = False
#~ playkeyframes = True
if fullscreen:
res = 1024, 768
else:
res = 720, 528
player= VPlayer(parsed[0].keyframes)
#~ player.record()
display = Display(fullscreen, player)
player.startPlayback( { 'name': videofile } )
player.start()
player.setOverlay( (0,0,res[0],res[1]) )
ev = EventLoop(display)
player.setEventLoop(ev)
ev.start(player)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment