def ProcessEditDecision(tmpFilePath, edit, width, height): """\ Prefab. Applies an edit decision - reading in the relevant video frames and applying the reframing. Outputs the reframed video frames out of the "outbox" outbox. Arguments: - tmpFilePath -- temp directory into which video frames have been saved - edit -- the edit instruction (dictionary containing: "start","end","left","top","right","bottom") - width -- width (in pixels) for output video frames - height -- height (in pixels) for output video frames Inboxes: - "inbox" -- NOT USED - "control" -- Shutdown signalling Outboxes: - "outbox" -- NOT USED - "signal" -- Shutdown signalling """ print " Video segment: ", edit filenames = [ tmpFilePath + "%08d.yuv" % i for i in range(edit["start"], edit["end"] + 1) ] newsize = (width, height) cropbounds = (edit["left"], edit["top"], edit["right"], edit["bottom"]) return Graphline( \ FILENAMES = ForwardIteratingChooser(filenames), FRAME_LOADER = Carousel( lambda filename : Pipeline( 2, MaxSpeedFileReader(filename,chunksize=1024*1024), 2, YUV4MPEGToFrame(), ), make1stRequest=False ), REFRAMING = Pipeline( 2, ToRGB_interleaved(), 2, CropAndScale(newsize, cropbounds), 2, ToYUV420_planar(), ), linkages = { ("FRAME_LOADER", "requestNext") : ("FILENAMES", "inbox"), ("FILENAMES", "outbox") : ("FRAME_LOADER", "next"), ("FRAME_LOADER", "outbox") : ("REFRAMING", "inbox"), ("REFRAMING", "outbox") : ("", "outbox"), ("FILENAMES", "signal") : ("FRAME_LOADER", "control"), ("FRAME_LOADER", "signal") : ("REFRAMING", "control"), ("REFRAMING", "signal") : ("", "signal"), }, boxsizes = { }, )
def DetermineMaxFrameNumber(edlfile): """\ Prefab. "outbox" sends out the highest frame number referenced in the EDL xml file. Then terminates immediately and sends out a producerFinished() message from the "signal" outbox. Arguments: - edlfile -- full filepathname of the EDL xml file Inboxes: - "inbox" -- NOT USED - "control" -- Shutdown signalling Outboxes: - "outbox" -- sends out the highest frame number referenced in the EDL file - "signal" -- Shutdown signalling """ return Pipeline( RateControlledFileReader(edlfile,readmode="lines",rate=1000000), SimpleXMLParser(), EDLParser(), SimpleDetupler("end"), Collate(), Max(), )
def AudioSplitterByFrames(framerate, channels, sample_rate, sample_format, tmpFilePath, edlfile): """\ Prefab. Saves raw audio data in the specified (chanels,sample_rate,sample_format) format sent to the "inbox" inbox into the specified temp directory. Chunks the audio into frames, as per the specified frame-rate. Only saves those frames actually referenced in the EDL file. Frames are saved in individual files in WAV format. They are named sequentially "00000001.wav", "00000002.wav", "00000003.wav", etc - being assigned frame numbers as they arrive, starting at 1. Arguments: - frame_rate -- the frame rate to chunk the audio into for saving - channels -- number of channels in the audio data - sample_rate -- sample rate of the audio data - sample_format -- sample format of the audio data - tmpFilePath -- temp directory into which frames should be saved - edlfile -- full filepathname of the EDL xml file Inboxes: - "inbox" -- raw audio data - "control" -- Shutdown signalling Outboxes: - "outbox" -- NOT USED - "signal" -- Shutdown signalling """ from Kamaelia.Support.PyMedia.AudioFormats import format2BytesPerSample quantasize = format2BytesPerSample[sample_format] * channels audioByteRate = quantasize * sample_rate return Pipeline( 10, RateChunker(datarate=audioByteRate, quantasize=quantasize, chunkrate=framerate), 1, TagWithSequenceNumber(), 1, FilterForWantedFrameNumbers(edlfile), 1, InboxControlledCarousel( lambda (framenum, audiochunk) : \ Pipeline( 1, OneShot(audiochunk), 1, WAVWriter(channels,sample_format,sample_rate), 1, SimpleFileWriter(tmpFilePath+("%08d.wav" % framenum)), ), boxsize=1, ), )
def FilterForWantedFrameNumbers(edlfile): """\ Prefab. Send messages of the form (framenum, data) to the "inbox" inbox. Items with a frame number that isn't in the edit decision list are dropped. Other items with frame numbers that are in the edit decision list are passed through out of the "outbox" outbox. Arguments: - edlfile -- full filepathname of the EDL xml file Inboxes: - "inbox" -- (framenum, data) items to be filtered - "control" -- Shutdown signalling Outboxes: - "outbox" -- items not filtered out - "signal" -- Shutdown signalling """ class ExtractRanges(object): def filter(self, edit): try: return (edit['start'],edit['end']) except: return None return Graphline( RANGESRC = Pipeline( RateControlledFileReader(edlfile,readmode="lines",rate=1000000), SimpleXMLParser(), EDLParser(), Filter(filter = ExtractRanges()), Collate(), ), FILTER = Carousel(lambda ranges : RangeFilter(ranges)), linkages = { ("RANGESRC","outbox") : ("FILTER","next"), ("","inbox") : ("FILTER","inbox"), ("FILTER","outbox") : ("","outbox"), ("","control") : ("FILTER","control"), ("FILTER","signal") :("","signal"), }, )
def PassThroughAudio(edlfile, tmpFilePath): """\ Prefab. Goes through the specified edit decision list file and reads in the audio frames corresponding to the video frames referred to in the reframing instructions in sequence. Outputs the audio frames out of the "outbox" outbox. Arguments: - edlfile -- full filepathname of the EDL xml file - tmpFilePath -- temp directory into which video frames have been saved Inboxes: - "inbox" -- NOT USED - "control" -- Shutdown signalling Outboxes: - "outbox" -- raw audio data, chunked by frames - "signal" -- Shutdown signalling """ backplane_name = "AUDIO_FORMAT" return Graphline( \ GET_EDL = EditDecisionSource(edlfile), AUDIO = Carousel( lambda edit : PassThroughAudioSegment(tmpFilePath, edit, backplane_name), make1stRequest=True), BACKPLANE = Backplane(backplane_name), AUDIOFORMAT = Pipeline( SubscribeTo(backplane_name), FirstOnly() ), linkages = { ("AUDIO", "requestNext") : ("GET_EDL", "inbox"), ("GET_EDL", "outbox") : ("AUDIO", "next"), ("AUDIO", "outbox") : ("", "outbox"), ("AUDIOFORMAT", "outbox") : ("", "audioformat"), ("GET_EDL", "signal") : ("AUDIO", "control"), ("AUDIO", "signal") : ("AUDIOFORMAT", "control"), ("AUDIOFORMAT", "signal") : ("BACKPLANE", "control"), ("BACKPLANE", "signal") : ("", "signal"), }, )
def EditDecisionSource(edlfile): """\ Prefab. Reads in the edit decisions from the edit decision list file; then sends then out, one at a time, out of the "outbox" outbox whenever a message is sent to the "inbox" inbox. The message sent to the inbox does not matter. Edit decisions are of the form:: { "start" : start frame number for this edit decision "end" : end frame number for this edit decision "left" : left edge to crop to (in pixels) "top" : top edge to crop to (in pixels) "right" : right edge to crop to (in pixels) "bottom" : bottom edge to crop to (in pixels) } Arguments: - edlfile -- full filepathname of the EDL xml file Inboxes: - "inbox" -- Messages to trigger sending out of edit decisions - "control" -- Shutdown signalling Outboxes: - "outbox" -- Individual edit decisions - "signal" -- Shutdown signalling """ return Graphline( \ PARSING = Pipeline( RateControlledFileReader(edlfile,readmode="lines",rate=1000000), SimpleXMLParser(), EDLParser(), ), GATE = PromptedTurnstile(), linkages = { ("", "inbox") : ("GATE", "next"), ("PARSING", "outbox") : ("GATE", "inbox"), ("GATE", "outbox") : ("", "outbox"), ("PARSING", "signal") : ("GATE", "control"), ("GATE", "signal") : ("", "signal"), } )
def SaveVideoFrames(tmpFilePath,edlfile): """\ Prefab. Saves video frames sent to the "inbox" inbox into the specified temp directory. Only saves those frames actually referenced in the EDL file. Frames are saved in individual files in YUV4MPEG2 format. They are named sequentially "00000001.yuv", "00000002.yuv", "00000003.yuv", etc - being assigned frame numbers as they arrive, starting at 1. Arguments: - tmpFilePath -- temp directory into which frames should be saved - edlfile -- full filepathname of the EDL xml file Inboxes: - "inbox" -- video frames to be saved - "control" -- Shutdown signalling Outboxes: - "outbox" -- NOT USED - "signal" -- Shutdown signalling """ return \ Pipeline( 1, TagWithSequenceNumber(), 1, FilterForWantedFrameNumbers(edlfile), 1, InboxControlledCarousel( lambda (framenum, frame) : \ Pipeline( OneShot(frame), 1, FrameToYUV4MPEG(), 1, SimpleFileWriter(tmpFilePath+("%08d.yuv" % framenum)), ), boxsize=1, ), )
) Graphline( DECODE=UnixProcess2( "ffmpeg -i " + infile + " -f yuv4mpegpipe -y vidpipe.yuv -f wav -y audpipe.wav", outpipes={ "vidpipe.yuv": "video", "audpipe.wav": "audio" }, buffersize=131072, ), VIDEO=Pipeline( 1, YUV4MPEGToFrame(), FrameRateLimitedPlayback(VideoOverlay()), ), AUDIO=Graphline( PARSE=WAVParser(), OUT=Carousel(lambda format: Output(format['sample_rate'], format['channels'], format['sample_format'], maximumLag=0.5)), linkages={ ("", "inbox"): ("PARSE", "inbox"), ("PARSE", "outbox"): ("OUT", "inbox"), ("PARSE", "all_meta"): ("OUT", "next"), ("", "control"): ("PARSE", "control"), ("PARSE", "signal"): ("OUT", "control"), ("OUT", "signal"): ("", "signal"),
" [--show] [threshold] videofile\n\n* threshold is a floating point value greater than zero (default=0.9)\n\n" ) sys.exit(1) infile = files[0].replace(" ", "\ ") if not show: # simple cut detector Pipeline( UnixProcess2( "ffmpeg -i " + infile + " -f yuv4mpegpipe -y /dev/stdout", 32768), 2, YUV4MPEGToFrame(), 1, TagWithSequenceNumber(), 1, DetectShotChanges(threshold), FormatOutput(), ConsoleEchoer(), StopSelector(waitForTrigger=True), ).run() else: # cut detector plus playback at the same time from Kamaelia.UI.Pygame.Display import PygameDisplay from Kamaelia.UI.Pygame.VideoOverlay import VideoOverlay from Kamaelia.Util.Backplane import Backplane, PublishTo, SubscribeTo from Kamaelia.Util.RateFilter import MessageRateLimit
class SlowOutputter(threadedcomponent): def __init__(self): super(SlowOutputter,self).__init__(queuelengths=1) Outboxes = { "outbox" : "", "signal" : "", "reqNext" : "", } def main(self): while 1: if self.dataReady("inbox"): print self.recv("inbox") t=time.time()+0.2 while t>time.time(): self.pause(t-time.time()) else: if self.dataReady("control"): self.send(self.recv("control"), "signal") return Pipeline( MaxSpeedFileReader("TestEDL.xml",chunksize=128), 1, SimpleXMLParser(), 1, SlowOutputter(), ).run()
yield 1 __kamaelia_components__ = (TwoWaySplitter, ) if __name__ == "__main__": from Kamaelia.Experimental.Chassis import Graphline, Pipeline from Kamaelia.Util.DataSource import DataSource from Kamaelia.Util.RateFilter import MessageRateLimit from Kamaelia.Util.Console import ConsoleEchoer Graphline(SRC=DataSource([str(i) + "\n" for i in range(0, 100)]), SPLIT=TwoWaySplitter(), DST1=Pipeline( 10, MessageRateLimit(10, 5), ConsoleEchoer(), ), DST2=Pipeline( 10, MessageRateLimit(20, 5), ConsoleEchoer(), ), linkages={ ("SRC", "outbox"): ("SPLIT", "inbox"), ("SPLIT", "outbox"): ("DST1", "inbox"), ("SPLIT", "outbox2"): ("DST2", "inbox"), ("SRC", "signal"): ("SPLIT", "control"), ("SPLIT", "signal"): ("DST1", "control"), ("SPLIT", "signal2"): ("DST2", "control"), },