From cf952f05cf7a07749801010e69a507b6fd128670 Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Wed, 18 Jul 2007 11:21:01 +0000 Subject: [PATCH] poster still --- oxdbarchive/cache.py | 8 ++ oxdbarchive/controllers.py | 4 + oxdbarchive/model.py | 35 ++++--- oxdbarchive/subtitles.py | 14 ++- oxdbarchive/tools/extract_poster_still.py | 108 ++++++++++++++++++++++ 5 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 oxdbarchive/tools/extract_poster_still.py diff --git a/oxdbarchive/cache.py b/oxdbarchive/cache.py index a58be8c..ab326c0 100644 --- a/oxdbarchive/cache.py +++ b/oxdbarchive/cache.py @@ -56,3 +56,11 @@ def loadTimeline(afile): if exists(timeline): return loadFile(timeline) return '' + +def loadPosterStill(afile, position): + still = afile.posterStillFile + if not exists(still): + afile.extractPosterStill(position) + if exsts(still): + return loadFile(still) + return '' diff --git a/oxdbarchive/controllers.py b/oxdbarchive/controllers.py index 199050e..8e14522 100644 --- a/oxdbarchive/controllers.py +++ b/oxdbarchive/controllers.py @@ -46,4 +46,8 @@ class Root(controllers.RootController): cherrypy.response.headerMap['Content-Type'] = "image/jpeg" cherrypy.response.headerMap["Expires"] = httpExpires(60*60*24*15) return f.frame(position) + elif action == 'posterStill': + cherrypy.response.headerMap['Content-Type'] = "image/png" + cherrypy.response.headerMap["Expires"] = httpExpires(60*60*24*15) + return f.posterStill(position) return dict() \ No newline at end of file diff --git a/oxdbarchive/model.py b/oxdbarchive/model.py index be4c074..0242439 100644 --- a/oxdbarchive/model.py +++ b/oxdbarchive/model.py @@ -305,15 +305,16 @@ class ArchiveFile(SQLObject): def updateMeta(self): self.findSubtitleLink() if exists(self.absolutePath): - info = midentify.identify(self.absolutePath) - self.length = info['length'] - self.width = info['width'] - self.frameAspect = "%0.6f" % info['aspect'] - self.height = info['height'] - self.bitrate = info['video_bitrate'] - self.fps = info['fps'] - self.audio = info['audio_codec'] - self.video = info['video_codec'] + if not self.broken: + info = midentify.identify(self.absolutePath) + self.length = info['length'] + self.width = info['width'] + self.frameAspect = "%0.6f" % info['aspect'] + self.height = info['height'] + self.bitrate = info['video_bitrate'] + self.fps = info['fps'] + self.audio = info['audio_codec'] + self.video = info['video_codec'] self.updatePixels() self.updateBpp() self.loadSubtitleFromFile() @@ -377,7 +378,7 @@ class ArchiveFile(SQLObject): def _get_mini_movie_file(self): return join(cache.cache_root, 'mini', self.md5sum[:4], "%s.avi" % self.md5sum) - + def _get_frameFolder(self): f = join(cache.cache_root, 'frame', self.md5sum[:4], self.md5sum) oxdb_makedir(f) @@ -385,7 +386,10 @@ class ArchiveFile(SQLObject): def _get_timelineFile(self): return join(cache.cache_root, 'timeline', self.md5sum[:4], "%s.png" % self.md5sum) - + + def _get_posterStillFile(self): + return join(cache.cache_root, 'posterStill', self.md5sum[:4], "%s.png" % self.md5sum) + def removeMiniMovie(self): if exists(self.mini_movie_file): os.remove(self.mini_movie_file) @@ -458,6 +462,9 @@ class ArchiveFile(SQLObject): for p in self._startPoints(): self.extractFrame(p) + def extractPosterStill(self, position, img_folder=): + extract_poster_still(self.movieFile, self.posterStillFile, position) + def extractClipMovie(self, force = False): if self.broken: return @@ -485,8 +492,7 @@ class ArchiveFile(SQLObject): cmd = "mencoder %s >/dev/null 2>&1" % options print cmd.encode('utf-8') os.system(cmd.encode('utf-8')) - - + def removeTimeline(self): if exists(self.timelineFile): os.unlink(self.timelineFile) @@ -582,3 +588,6 @@ class ArchiveFile(SQLObject): def timeline(self): return cache.loadTimeline(self) + + def posterStill(self, position): + return cache.loadPosterStill(self, position) diff --git a/oxdbarchive/subtitles.py b/oxdbarchive/subtitles.py index c64c79f..d21f5dc 100644 --- a/oxdbarchive/subtitles.py +++ b/oxdbarchive/subtitles.py @@ -219,8 +219,18 @@ def extract_frame(movie_file, timestamp, img_folder, width=128, offset = 0, redo else: print "update the cache %s missing" % movie_file shutil.rmtree(framedir) - - + +def extract_poster_still(movie_file, png_file, inpoint): + ext = movie_file.split('.')[-1] + if ext in ('sub', 'srt'): + print "this is not a movie file, will not try to extract frames" + return + inpoint = time_str2msec(inpoint) + extractClipScript = abspath(join(dirname(__file__), "tools/extract_poster_still.py")) + + cmd = '''%s "%s" "%s" %s''' % (extractClipScript, movie_file, png_file, inpoint) + os.system(cmd.encode('utf-8')) + def extract_subtitles(movie_file, srt, img_folder, width=128, offset = 0, redo = False): subtitles = srt2dict(srt) for k in sorted([int(k) for k in subtitles.keys()]): diff --git a/oxdbarchive/tools/extract_poster_still.py b/oxdbarchive/tools/extract_poster_still.py new file mode 100644 index 0000000..f8c9db5 --- /dev/null +++ b/oxdbarchive/tools/extract_poster_still.py @@ -0,0 +1,108 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# -*- Mode: Python; -*- +# vi:si:et:sw=2:sts=2:ts=2 +# +import os +import time + +import gobject +gobject.threads_init() + +import pygst +pygst.require("0.10") +import gst +import Image + +class PosterStill: + getShot = False + length = 0 + height = 0 + def __init__(self, videofile, png='', nseconds=-1, height=128): + s = ''' filesrc name=input + ! decodebin name=dbin + ! queue name =q + ! ffmpegcolorspace ! video/x-raw-rgb + ! fakesink name=output signal-handoffs=true + ''' + + self.height = height + self.pipeline = gst.parse_launch(s) + self.input = self.pipeline.get_by_name('input') + self.fakesink = self.pipeline.get_by_name('output') + self.dbin = self.pipeline.get_by_name('dbin') + self.bus = self.pipeline.get_bus() + self.input.props.location = videofile + + self.pipeline.set_state(gst.STATE_PAUSED) + self.pipeline.get_state() + + #length + queue = self.pipeline.get_by_name('q') + pads = queue.sink_pads() + q = gst.query_new_duration(gst.FORMAT_TIME) + for pad in pads: + if pad.get_peer() and pad.get_peer().query(q): + format, self.length = q.parse_duration() + + if nseconds>-1 and png: + self.png(png, nseconds) + + def close(self): + self.pipeline.set_state(gst.STATE_NULL) + self.pipeline.get_state() + + def seek(self, nseconds): + if(self.length and self.length < nseconds): + nseconds = 0 + event = gst.event_new_seek(1.0, gst.FORMAT_TIME, + gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, + gst.SEEK_TYPE_SET, nseconds, + gst.SEEK_TYPE_NONE, 0) + + res = self.fakesink.send_event(event) + if res: + self.pipeline.set_new_stream_time(0L) + else: + gst.error("seek to %r failed" % nseconds) + + def png(self, png, nseconds): + self.png_frame = png + self.pipeline.set_state(gst.STATE_PAUSED) + self.pipeline.get_state() + self.seek(nseconds) + self.pipeline.set_state(gst.STATE_PLAYING) + self.pipeline.get_state() + + ho = self.fakesink.connect("handoff", self.snapshot_png_handoff_cb) + self.getShot = True + while self.getShot: + msg = self.bus.poll(gst.MESSAGE_ANY, gst.SECOND) + if not msg: + break + self.fakesink.disconnect(ho) + + def snapshot_png_handoff_cb(self, sink, buffer, pad): + if self.getShot: + caps = sink.sink_pads().next().get_negotiated_caps() + for s in caps: + input_d = (s['width'], s['height']) + output_d = self.scaleto(s['width'], s['height']) + img = Image.fromstring('RGB',input_d,buffer) + img = img.resize(output_d, Image.ANTIALIAS) + img.save(self.png_frame) + self.getShot=False + + def scaleto(self, width, height): + width = int(self.height * (float(width) / height)) + width = width - width%2 + self.width = width + return (self.width, self.height) + +if __name__ == "__main__": + import sys + height = 128 + inputFile = sys.argv[1] + outputFile = sys.argv[2] + offset = int(float(sys.argv[3]) * gst.MSECOND) + f = PosterStill(inputFile, outputFile, offset, height)