poster still
This commit is contained in:
parent
894d0399f0
commit
cf952f05cf
5 changed files with 154 additions and 15 deletions
|
@ -56,3 +56,11 @@ def loadTimeline(afile):
|
||||||
if exists(timeline):
|
if exists(timeline):
|
||||||
return loadFile(timeline)
|
return loadFile(timeline)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
def loadPosterStill(afile, position):
|
||||||
|
still = afile.posterStillFile
|
||||||
|
if not exists(still):
|
||||||
|
afile.extractPosterStill(position)
|
||||||
|
if exsts(still):
|
||||||
|
return loadFile(still)
|
||||||
|
return ''
|
||||||
|
|
|
@ -46,4 +46,8 @@ class Root(controllers.RootController):
|
||||||
cherrypy.response.headerMap['Content-Type'] = "image/jpeg"
|
cherrypy.response.headerMap['Content-Type'] = "image/jpeg"
|
||||||
cherrypy.response.headerMap["Expires"] = httpExpires(60*60*24*15)
|
cherrypy.response.headerMap["Expires"] = httpExpires(60*60*24*15)
|
||||||
return f.frame(position)
|
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()
|
return dict()
|
|
@ -305,15 +305,16 @@ class ArchiveFile(SQLObject):
|
||||||
def updateMeta(self):
|
def updateMeta(self):
|
||||||
self.findSubtitleLink()
|
self.findSubtitleLink()
|
||||||
if exists(self.absolutePath):
|
if exists(self.absolutePath):
|
||||||
info = midentify.identify(self.absolutePath)
|
if not self.broken:
|
||||||
self.length = info['length']
|
info = midentify.identify(self.absolutePath)
|
||||||
self.width = info['width']
|
self.length = info['length']
|
||||||
self.frameAspect = "%0.6f" % info['aspect']
|
self.width = info['width']
|
||||||
self.height = info['height']
|
self.frameAspect = "%0.6f" % info['aspect']
|
||||||
self.bitrate = info['video_bitrate']
|
self.height = info['height']
|
||||||
self.fps = info['fps']
|
self.bitrate = info['video_bitrate']
|
||||||
self.audio = info['audio_codec']
|
self.fps = info['fps']
|
||||||
self.video = info['video_codec']
|
self.audio = info['audio_codec']
|
||||||
|
self.video = info['video_codec']
|
||||||
self.updatePixels()
|
self.updatePixels()
|
||||||
self.updateBpp()
|
self.updateBpp()
|
||||||
self.loadSubtitleFromFile()
|
self.loadSubtitleFromFile()
|
||||||
|
@ -386,6 +387,9 @@ class ArchiveFile(SQLObject):
|
||||||
def _get_timelineFile(self):
|
def _get_timelineFile(self):
|
||||||
return join(cache.cache_root, 'timeline', self.md5sum[:4], "%s.png" % self.md5sum)
|
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):
|
def removeMiniMovie(self):
|
||||||
if exists(self.mini_movie_file):
|
if exists(self.mini_movie_file):
|
||||||
os.remove(self.mini_movie_file)
|
os.remove(self.mini_movie_file)
|
||||||
|
@ -458,6 +462,9 @@ class ArchiveFile(SQLObject):
|
||||||
for p in self._startPoints():
|
for p in self._startPoints():
|
||||||
self.extractFrame(p)
|
self.extractFrame(p)
|
||||||
|
|
||||||
|
def extractPosterStill(self, position, img_folder=):
|
||||||
|
extract_poster_still(self.movieFile, self.posterStillFile, position)
|
||||||
|
|
||||||
def extractClipMovie(self, force = False):
|
def extractClipMovie(self, force = False):
|
||||||
if self.broken:
|
if self.broken:
|
||||||
return
|
return
|
||||||
|
@ -486,7 +493,6 @@ class ArchiveFile(SQLObject):
|
||||||
print cmd.encode('utf-8')
|
print cmd.encode('utf-8')
|
||||||
os.system(cmd.encode('utf-8'))
|
os.system(cmd.encode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
def removeTimeline(self):
|
def removeTimeline(self):
|
||||||
if exists(self.timelineFile):
|
if exists(self.timelineFile):
|
||||||
os.unlink(self.timelineFile)
|
os.unlink(self.timelineFile)
|
||||||
|
@ -582,3 +588,6 @@ class ArchiveFile(SQLObject):
|
||||||
|
|
||||||
def timeline(self):
|
def timeline(self):
|
||||||
return cache.loadTimeline(self)
|
return cache.loadTimeline(self)
|
||||||
|
|
||||||
|
def posterStill(self, position):
|
||||||
|
return cache.loadPosterStill(self, position)
|
||||||
|
|
|
@ -220,6 +220,16 @@ def extract_frame(movie_file, timestamp, img_folder, width=128, offset = 0, redo
|
||||||
print "update the cache %s missing" % movie_file
|
print "update the cache %s missing" % movie_file
|
||||||
shutil.rmtree(framedir)
|
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):
|
def extract_subtitles(movie_file, srt, img_folder, width=128, offset = 0, redo = False):
|
||||||
subtitles = srt2dict(srt)
|
subtitles = srt2dict(srt)
|
||||||
|
|
108
oxdbarchive/tools/extract_poster_still.py
Normal file
108
oxdbarchive/tools/extract_poster_still.py
Normal file
|
@ -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)
|
Loading…
Reference in a new issue