Source code for mg.media.video.models

# MG_GPL_HEADER_BEGIN
# 
# This file is part of Media Gallery, GNU GPLv2.
# 
# Media Gallery is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
# 
# Media Gallery is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with Media Gallery; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# 
# MG_GPL_HEADER_END

__author__ = "Media Gallery contributors <mg@lists.ssji.net>"
__copyright__ = "(c) 2010"
__credits__ = ["Dimitri Refauvelet", "Nicolas Pouillon"]


from django.db import models

from mg.core.element.models import Element

__all__ = ['Video', 'VideoFormat', 'ResizedVideo']

BOTTOM_SIDE = (
    ('D', 'Default'),
    ('B', 'Bottom'),
    ('L', 'Left'),
    ('T', 'Top'),
    ('R', 'Right'),
    )

class Video(Element):
[docs] width = models.IntegerField() height = models.IntegerField() ratio = models.FloatField(db_index = True) dar = models.FloatField() largest = models.IntegerField(db_index = True) file = models.FileField(upload_to = "spool") afmt = models.CharField(max_length = 16, blank = True) vfmt = models.CharField(max_length = 16, blank = True) cfmt = models.CharField(max_length = 16, blank = True) bottom_side = models.CharField(db_index = True, max_length = 1, choices = BOTTOM_SIDE, default = "D") key_time = models.IntegerField(null = True, blank = True) def save(self, *args, **kwargs): if self.pk is None: self.width = self.height = self.ratio = self.dar = self.largest = 0 super(Video, self).save(*args, **kwargs) self._rehash_size() super(Video, self).save(*args, **kwargs) def _rehash_size(self): vh = self.get_handler() try: vs = vh.get_streams('video')[0] except Exception, e: return self.width, self.height = vs.size self.vfmt = vs.codec self.cfmt = vh.container self.dar = vs.dar try: as_ = vh.get_streams('audio')[0] self.afmt = as_.codec except: pass self.largest = max((self.width, self.height)) self.ratio = float(self.width) / self.height def get_handler(self): from mg.utils.video import VideoHandler from mg.utils.file import local_file_from_storage filename, fd = local_file_from_storage(self.file, "."+self.cfmt) vh = VideoHandler(filename) # Take a ref on the tmp file, make it live as long as the # handler lives vh.__filename = filename vh.__fd = fd return vh @classmethod def __get_resizer(cls): from mg.utils.resizer import resizer return resizer def resized_get(self, **filt): return self.resizedvideo_set.filter(**filt).order_by('size__width')[0] def thumbnail_new(self, size): from mg.core.element.models import Thumbnail import tempfile from django.core.files.base import File r = self.__get_resizer() r.set_output_size(size = size.wh, mode = r.PAN_SCAN) # Get a temporary image size, taking DAR in account if self.dar > 1: w = size.height * self.dar h = size.height else: w = size.width h = size.width / self.dar # First do video -> image (of native size) vh = self.get_handler() tmp = tempfile.NamedTemporaryFile(suffix = ".jpg", mode = "w+b") vh.snapshot(tmp.name, rot_count = self._get_rot(), time_offset = self.key_time, image_size = (w, h)) # Now use the image resizer with bbox fitting to thumbnail out = tempfile.NamedTemporaryFile(suffix = ".jpg", mode = "w+b") r.resize(out.name, tmp.name, rot_count = 0) # Create the thumbnail with this dst = Thumbnail(size = size, element = self) dst.save() # And of course save the payload dst.file.save(dst.filename_gen(), File(out)) dst.save() return dst def __unicode__(self): return u"%s"%(self.name) def _get_rot(self): """ Returns the number of rotations of 90 degrees, clockwise, to apply to image """ # If bottom side of scene is on right side (R) of picture, we # have to rotate the image once clockwise, so R=1, others are # trivial. return "BRTLD".find(self.bottom_side) % 4 def rehash_all_resized(self): from mg.core.element.models import ThumbnailSize self.thumbnail_set.all().delete() self.resizedvideo_set.all().delete() for s in VideoFormat.objects.all(): ResizedVideo.forge(self, s) for s in ThumbnailSize.objects.all(): self.thumbnail_new(s) def info(self): h = self.duration / 3600 m = (self.duration / 60) % 60 s = self.duration % 60 r = Element.info(self) if r: r += ", " return r + '%02d:%02d:%02d' % (h, m, s) Element.register_type("video", Video)
class VideoFormat(models.Model):
[docs] width = models.IntegerField(db_index = True) height = models.IntegerField(db_index = True) afmt = models.CharField(max_length = 16) vfmt = models.CharField(max_length = 16) cfmt = models.CharField(max_length = 16) fps = models.FloatField() vbr = models.IntegerField() arate = models.IntegerField() achannels = models.IntegerField() add_flags = models.CharField(max_length = 128, blank = True) def __unicode__(self): return u"%dx%d %s %s %s"%(self.width, self.height, self.vfmt, self.afmt, self.cfmt) class Meta: ordering = ("-width",) @property def wh(self):
[docs] """ property corresponding to (width, height) of video size """ return self.width, self.height def preset(self, orig_size):
[docs] from mg.utils.video import VideoStreamInfo, AudioStreamInfo from mg.media.photo.utils import bbox_fitter foo, (x, y, w, h) = bbox_fitter.fit_resized(orig_size, self.wh, False) return dict( container = self.cfmt, vs_info = VideoStreamInfo(self.vfmt, (w,h), self.fps), as_info = AudioStreamInfo(self.afmt, self.arate, self.achannels, 16), add_flags = self.add_flags, ) class ResizedVideo(models.Model):
[docs] video = models.ForeignKey(Video) size = models.ForeignKey(VideoFormat) file = models.ImageField(upload_to = "resized") bottom_side = models.CharField(db_index = True, max_length = 1, choices = BOTTOM_SIDE, default = "D") def filename_gen(self):
[docs] base = u"%s_%s"%(self.video.name, self.size) return self.file.storage.get_available_name( self.file.storage.get_valid_name(base+"."+self.size.cfmt)) class Meta:
unique_together = ( ('video', 'size'), ) def __unicode__(self): return u"<%s @%s>"%(self.video, self.size) @classmethod def forge(cls, source, format): import tempfile from django.core.files.base import File # dar redizing is done before rotation. then we dont care # about choosing between (w, w*dar) and (w, w/dar) h = int((source.width / source.dar)+.5) preset = format.preset((source.width, h)) vh = source.get_handler() # Do video conversion tmp = tempfile.NamedTemporaryFile(suffix = "."+preset['container'], mode = "w+b") vh.transcode(tmp.name, preset['container'], preset['vs_info'], preset['as_info'], source._get_rot(), preset['add_flags'], ) # Create the object with this dst = cls(size = format, video = source, bottom_side = source.bottom_side) dst.save() # And of course save the payload dst.file.save(dst.filename_gen(), File(tmp)) dst.save() return dst