use extract_frame from pad.ma

This commit is contained in:
j 2008-07-03 18:40:06 +02:00
parent ba183e2dfd
commit 5f9a4ab588
2 changed files with 154 additions and 164 deletions

View File

@ -169,7 +169,7 @@ def extract_still(movie_file, png_file, inpoint):
inpoint = time2ms(inpoint) inpoint = time2ms(inpoint)
extractClipScript = abspath(join(dirname(__file__), "tools/extract_frame.py")) extractClipScript = abspath(join(dirname(__file__), "tools/extract_frame.py"))
cmd = '''%s "%s" "%s" %s 0 -1''' % (extractClipScript, movie_file, png_file, inpoint) cmd = '''%s "%s" "%s" %s 0 -1''' % (extractClipScript, movie_file, png_file, inpoint)
run_command(cmd.encode('utf-8')) run_command(cmd.encode('utf-8'), 100)
def extract_poster_still(movie_file, png_file, inpoint): def extract_poster_still(movie_file, png_file, inpoint):
ext = movie_file.split('.')[-1] ext = movie_file.split('.')[-1]

View File

@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vi:si:et:sw=2:sts=2:ts=2 # vi:si:et:sw=4:sts=4:ts=4
# GPL written 2008 by j@pad.ma # GPL 2008
import gobject import gobject
gobject.threads_init() gobject.threads_init()
@ -14,187 +14,177 @@ import Image
DEBUG=0 DEBUG=0
class ExtractFrame: class ExtractFrame:
video = None video = None
audio = None audio = None
info = {} info = {}
frame_img = None frame_img = None
frame_size = None frame_size = None
duration = 0 duration = 0
height = 0 height = 0
width = 128 width = 128
def __init__(self, videofile, frame, frame_pos, width=128, height=0): def __init__(self, videofile, frame, frame_pos, width=128):
self.width = width self.width = width
self.height = height self.frame_file = frame
self.frame_file = frame self.frame_pos = frame_pos
self.frame_pos = frame_pos self.mainloop = gobject.MainLoop()
self.mainloop = gobject.MainLoop() self.pipeline = gst.parse_launch('filesrc name=input ! decodebin name=dbin')
self.pipeline = gst.parse_launch('filesrc name=input ! decodebin name=dbin') self.input = self.pipeline.get_by_name('input')
self.input = self.pipeline.get_by_name('input') self.input.props.location = videofile
self.input.props.location = videofile self.dbin = self.pipeline.get_by_name('dbin')
self.dbin = self.pipeline.get_by_name('dbin')
self.bus = self.pipeline.get_bus() self.bus = self.pipeline.get_bus()
self.dbin.connect('new-decoded-pad', self.demux_pad_added) self.dbin.connect('new-decoded-pad', self.demux_pad_added)
self.bus.add_signal_watch() self.bus.add_signal_watch()
self.watch_id = self.bus.connect("message", self.onBusMessage) self.watch_id = self.bus.connect("message", self.onBusMessage)
self.pipeline.set_state(gst.STATE_PAUSED) self.pipeline.set_state(gst.STATE_PAUSED)
self.pipeline.get_state() self.pipeline.get_state()
def run(self): def run(self):
self.pipeline.set_state(gst.STATE_PLAYING) self.pipeline.set_state(gst.STATE_PLAYING)
self.pipeline.get_state() self.pipeline.get_state()
#duration #duration
pads = None pads = None
if self.video: if self.video:
pads = self.video.sink_pads() pads = self.video.sink_pads()
elif self.audio: elif self.audio:
pads = self.audio.sink_pads() pads = self.audio.sink_pads()
if pads: if pads:
q = gst.query_new_duration(gst.FORMAT_TIME) q = gst.query_new_duration(gst.FORMAT_TIME)
for pad in pads: for pad in pads:
if pad.get_peer() and pad.get_peer().query(q): if pad.get_peer() and pad.get_peer().query(q):
format, self.duration = q.parse_duration() format, self.duration = q.parse_duration()
self.info["duration"] = self.duration/gst.MSECOND self.info["duration"] = self.duration/gst.MSECOND
#seek #seek
if self.frame_pos > self.duration: if self.frame_pos > self.duration:
self.debug('seek point greater than file duration %s' % (self.duration/gst.MSECOND)) self.debug('seek point greater than file duration %s' % (self.duration/gst.MSECOND))
return return
if (self.duration - self.frame_pos) < gst.SECOND: if (self.duration - self.frame_pos) < gst.SECOND:
seek_pos = self.duration- 10 * gst.SECOND seek_pos = self.duration- 10 * gst.SECOND
else: else:
seek_pos = self.frame_pos - 10 * gst.SECOND seek_pos = self.frame_pos - 10 * gst.SECOND
seek_pos = max(0, seek_pos) seek_pos = max(0, seek_pos)
#extract #extract
self.debug('seek tp %s'%seek_pos) self.debug('seek tp %s'%seek_pos)
self.seek(seek_pos) self.seek(seek_pos)
self.debug('get frame tp %s'%seek_pos) self.debug('get frame tp %s'%seek_pos)
self.frame_ho = self.video.connect("handoff", self.get_frame_cb) self.frame_ho = self.video.connect("handoff", self.get_frame_cb)
self.pipeline.set_state(gst.STATE_PLAYING) self.pipeline.set_state(gst.STATE_PLAYING)
self.pipeline.get_state() self.pipeline.get_state()
self.mainloop.run() self.mainloop.run()
return self.info return self.info
def seek(self, seek_pos): def seek(self, seek_pos):
event = gst.event_new_seek(1.0, gst.FORMAT_TIME, event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE,
gst.SEEK_TYPE_SET, seek_pos, gst.SEEK_TYPE_SET, seek_pos,
gst.SEEK_TYPE_NONE, 0) gst.SEEK_TYPE_NONE, 0)
res = self.video.send_event(event) res = self.video.send_event(event)
if res: if res:
self.pipeline.set_new_stream_time(0L) self.pipeline.set_new_stream_time(0L)
else: else:
gst.error("seek to %r failed" % frame_pos) gst.error("seek to %r failed" % frame_pos)
def get_frame_cb(self, sink, frame_buffer, pad): def get_frame_cb(self, sink, frame_buffer, pad):
caps = sink.sink_pads().next().get_negotiated_caps() caps = sink.sink_pads().next().get_negotiated_caps()
for s in caps: for s in caps:
input_size = (s['width'], s['height']) input_size = (s['width'], s['height'])
if self.width > 0: if self.width > 0:
self.frame_size = self.scaleto(s['width'], s['height']) self.frame_size = self.scaleto(s['width'], s['height'])
else: else:
self.frame_size = None self.frame_size = None
#Why are the last frames broken, aka have green pixels #Why are the last frames broken, aka have green pixels
save_last_frame = (4*gst.SECOND/float(s['framerate'])) save_last_frame = (4*gst.SECOND/float(s['framerate']))
if (self.duration-self.frame_pos) < save_last_frame: if (self.duration-self.frame_pos) < save_last_frame:
self.frame_pos = self.duration-save_last_frame self.frame_pos = self.duration-save_last_frame
position, format = sink.query_position(gst.FORMAT_TIME) position, format = sink.query_position(gst.FORMAT_TIME)
if not self.frame_img or position <= self.frame_pos: if not self.frame_img or position <= self.frame_pos:
self.frame_img = Image.fromstring('RGB', input_size, frame_buffer) self.frame_img = Image.fromstring('RGB', input_size, frame_buffer)
else: else:
self.video.disconnect(self.frame_ho) self.video.disconnect(self.frame_ho)
self.bus.post(gst.message_new_eos(self.pipeline)) self.bus.post(gst.message_new_eos(self.pipeline))
def scaleto(self, width, height): def scaleto(self, width, height):
if self.width: height = int(self.width / (float(width) / height))
height = int(self.width / (float(width) / height)) height = height - height % 2
height = height - height % 2 self.height = height
return (self.width, height) return (self.width, height)
else:
width = int(self.height * (float(width) / height))
width = width - width % 2
return (width, self.height)
def get_audio_info_cb(self, sink, buffer, pad):
caps = sink.sink_pads().next().get_negotiated_caps()
for s in caps:
self.info["channels"] = s['channels']
self.info["samplerate"] = s['rate']
self.audio.disconnect(self.audio_cb)
def get_audio_info_cb(self, sink, buffer, pad): def get_frame_info_cb(self, sink, buffer, pad):
caps = sink.sink_pads().next().get_negotiated_caps() caps = sink.sink_pads().next().get_negotiated_caps()
for s in caps: for s in caps:
self.info["channels"] = s['channels'] self.info["width"] = s['width']
self.info["samplerate"] = s['rate'] self.info["height"] = s['height']
self.audio.disconnect(self.audio_cb) self.info["framerate"] = float(s['framerate'])
self.video.disconnect(self.video_cb)
def get_frame_info_cb(self, sink, buffer, pad): def demux_pad_added(self, element, pad, bool):
caps = sink.sink_pads().next().get_negotiated_caps() caps = pad.get_caps()
for s in caps: structure = caps[0]
self.info["width"] = s['width'] stream_type = structure.get_name()
self.info["height"] = s['height'] if stream_type.startswith('video'):
self.info["framerate"] = float(s['framerate']) colorspace = gst.element_factory_make("ffmpegcolorspace");
self.info["pixel-aspect-ratio"] = "%d:%d" % (s['pixel-aspect-ratio'].num, s['pixel-aspect-ratio'].denom) self.pipeline.add (colorspace);
self.video.disconnect(self.video_cb) colorspace.set_state (gst.STATE_PLAYING);
pad.link (colorspace.get_pad("sink"));
def demux_pad_added(self, element, pad, bool): self.video = gst.element_factory_make("fakesink")
caps = pad.get_caps() self.video.props.signal_handoffs = True
structure = caps[0] self.pipeline.add(self.video)
stream_type = structure.get_name() self.video.set_state (gst.STATE_PLAYING);
if stream_type.startswith('video'): colorspace.link (self.video, gst.caps_from_string('video/x-raw-rgb'));
colorspace = gst.element_factory_make("ffmpegcolorspace"); self.video_cb = self.video.connect("handoff", self.get_frame_info_cb)
self.pipeline.add (colorspace); elif stream_type.startswith('audio'):
colorspace.set_state (gst.STATE_PLAYING); self.audio = gst.element_factory_make("fakesink")
pad.link (colorspace.get_pad("sink")); self.audio.props.signal_handoffs = True
self.pipeline.add(self.audio)
self.audio.set_state (gst.STATE_PLAYING);
pad.link(self.audio.get_pad('sink'))
self.audio_cb = self.audio.connect("handoff", self.get_audio_info_cb)
self.video = gst.element_factory_make("fakesink") def onBusMessage(self, bus, message):
self.video.props.signal_handoffs = True if message.src == self.pipeline and message.type == gst.MESSAGE_EOS:
self.pipeline.add(self.video) self.quit()
self.video.set_state (gst.STATE_PLAYING);
colorspace.link (self.video, gst.caps_from_string('video/x-raw-rgb'));
self.video_cb = self.video.connect("handoff", self.get_frame_info_cb)
elif stream_type.startswith('audio'):
self.audio = gst.element_factory_make("fakesink")
self.audio.props.signal_handoffs = True
self.pipeline.add(self.audio)
self.audio.set_state (gst.STATE_PLAYING);
pad.link(self.audio.get_pad('sink'))
self.audio_cb = self.audio.connect("handoff", self.get_audio_info_cb)
def onBusMessage(self, bus, message): def quit(self):
if message.src == self.pipeline and message.type == gst.MESSAGE_EOS: if self.frame_img:
self.quit() if self.frame_size
img = self.frame_img.resize(self.frame_size, Image.ANTIALIAS)
else:
img = self.frame_img
img.save(self.frame_file)
self.debug('frame saved at %s' % self.frame_file)
self.pipeline.set_state(gst.STATE_NULL)
self.pipeline.get_state()
self.mainloop.quit()
def quit(self): def debug(self, msg):
if self.frame_img: if DEBUG:
if self.frame_size: print msg
img = self.frame_img.resize(self.frame_size, Image.ANTIALIAS)
else:
img = self.frame_img
img.save(self.frame_file)
self.debug('frame saved at %s' % self.frame_file)
self.pipeline.set_state(gst.STATE_NULL)
self.pipeline.get_state()
self.mainloop.quit()
def debug(self, msg):
if DEBUG:
print msg
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
width = 128 width = 128
height = 0 inputFile = sys.argv[1]
inputFile = sys.argv[1] outputFile = sys.argv[2]
outputFile = sys.argv[2] offset = int(float(sys.argv[3]) * gst.MSECOND)
offset = int(float(sys.argv[3]) * gst.MSECOND) if len(sys.argv) > 4:
if len(sys.argv) > 4: width = int(sys.argv[4])
width = int(sys.argv[4]) f = ExtractFrame(inputFile, outputFile, offset, width)
if len(sys.argv) > 5: f.run()
height = int(sys.argv[5])
f = ExtractFrame(inputFile, outputFile, offset, width, height)
f.run()