使用mplayer做影片预览图

来源:互联网 发布:mac版炒股软件哪个好 编辑:程序博客网 时间:2024/05/01 22:35
影片预览图就是从整部影片中截取若干张图以求反映整部影片的全貌。就像下面这张图(来自<灌篮高手>剧场版中的那部 湘北 VS 绿风)


-- ./sd.jpg --



mplayer是一款在GNU GPL下发布的自由软件,一款跨平台的,相当牛B的多媒体播放器。

当面mplayer也可以像windows下的多数播放器那样有个像样的GUI前端,如果你觉得有必要的话。单是播放视频,那个GUI绝对是多余的,做其它事,GUI前端也无能为力。所有,一般比较多地是在命令行下使用mplayer,这样你可以用它解决很多问题。比如设置视频解码,音频解码,单独设置视频音频的输出,截取视频音频文件,合并视频文件,输出媒体信息等等等等(有些功能要配合mencoder)。

要做到上图的效果,我用到了下面这些工具(或许你自己比较喜欢用其它的):

  • mplayer
  这个不必多说。它负责在影片的指定位置截取图像。
  你可以从它的官方网站获取安装程序,当然,同时你还应该从官网上下载解码包才能解码大多数媒体格式。

  • imagemagick
  强大的图像编辑软件,很多编程语言都有其接口。它负责把mplayer截取的图片进行进一步的处理。比如上图中,把十张图排成两列,每张图的右下角还加了时间。它的功能是处理图,所有你也可以把这些图片用其它方式呈现(后面有举例)。它官方网站上面也有windows的安装包。

  • python
  用起来很简单很舒服的编程语言。我用它来操作上面两个软件的一系列操作。还有写成脚本也可以重复利用嘛。可以从官方网站下载安装包直接安装。

用mplayer截图

其实这个截图,说起来是很简单的。定位到某时间点,截图。再说细点,定位到某时间点,输出一帧,输出方式为png或jpeg。就是这样,还有就是不需要音频输出。
复制内容到剪贴板
代码:
        mplayer -ss 84 -noframedrop -nosound -vo png -frames 1 xx.rmvb
-ss 指定起始位置,这里是第84秒的地方。
-noframedrop 不跳过帧(即使解码速度跟不上),-nosound 没有声音输出(不对声音进行解码)。
-vo 视频输出方式,这里指定的是png,也可以是jpeg。这样输出的就是图片了。
-frames 输出多少帧。我们只截一张图就够了,所以指定1。我的动画的rmvb一秒是24帧。

载图就是这样的,细节部分在后面讨论,如怎么去获取这部影片一共有多长时间,又怎么知道在哪些地方截图。

用imagemagick处理图片

我们假设我们要截的图都做好了,剩下的工作是如果用imagemagick来处理这些图,比如在图的右下角加上时间,把N多图排成两列。

        右下角加上时间

这个工作是这样完成的。把一些文本,设置好一些属性如颜色,背景色,放到图片中指定的位置。使用imagemagick中的convert加上annotate参数就可以做到。
复制内容到剪贴板
代码:
        convert xx01.png -fill black -undercolor white -pointsize 40 -gravity SouthEast -annotate +5+5 '18:45:13' xx01.jpg
xx01.png 是要处理的图片,xx01.jpg 是处理后输出的图片。
-fill 用于文本的颜色,这里是黑色。-undercolor 是文本的背景色,这里设成白色。
-pointsize 用于设置文本的大小。-gravity 文本放置的位置,九宫格参数,这里设的是右下。
-annotate 放置文本,后面跟文本的位置偏移,+5+5指横竖位置都有5的移动。
'18:45:13' 是要放置的文本。

这里有个问题就是,这个时间的文本怎么获取。后面讨论。

        N多图排成两列

使用imagemagick的montage工具,可以把N张图拼到一起。
复制内容到剪贴板
代码:
        montage *.png -resize 290 -geometry +5+5 -tile 2x montage.jpg
*.png 就是把当前目录所有的png文件拼到一起。
-resize 把原来的每张png图片,宽度改为290。
-geometry 排列的位置, +5+5指图片间横竖都有5的间隔。
-tile 指定图片排列的方式,几列几行。这里设为2列,行数自动。
montage.jpg 是输出的拼合后的文件。

如何获取影片的长度

假如我们要在整部影片中截10张图,那么这10张图应该在哪些时间点截呢?要回答这个问题,我们要知道整部影片有多长。mplayer可以直接获取这些信息。
复制内容到剪贴板
代码:
        mplayer -identify -nosound -vc dummy -vo null xx.rmvb
这个命令不会去播放xx.rmvb,只是输出相关信息。在输出的一大堆信息中,有一条ID_LENGTH=XXXX就是这部影片的长度。用python解析这堆输出就可以了。

有了整部影片的长度,就知道了截取的起始点与终点,通过spec=(end-start)/n-1就算出,你要截n张图的话,应该每隔spec秒截一张。进一步,第n张图的载取点在第start+spec * (n-1)秒处。

用python实现吧

你可以不用管怎么用python,也可以不用管什么脚本,只不过麻烦点。因为上面讲的一步步都要手工去做而已。

现在需要明白的都明白了,剩下的就是用python写出执行脚本就OK了。

(代码在楼下)

上面的extra2extra3对图片做了另外两种处理。如下图:(extra3的GIF图传不上来了,最后有个GIF例子)


-- ./sd-overlap.jpg --



更多

我觉得提取影片预览图的应用比较多。

放到服务器,对上传的视频进行预览。本机上,通过与文件管理器,窗口管理器的协作也可以实现很多功能,比如让视频像音乐专辑那样做归类封面。

变通一下,这个mplayer也可以截取影片的一段做成GIF动画。

实现的方法说一下,从某点开始截图,截N帧,也就是N张图。rmvb一秒24帧要多少帧可以算出。然后对几百张图进行等差删除(小技巧:控制文件管理器的窗口大小,就可以等差选取一大片排列好的图片文件),之后把剩下的图用extra3的方法做成GIF就可以了。如下图:


#! /usr/bin/python
# -*- coding: UTF-8 -*-

import os
import sys

FRONT = 180 #前面多少秒无视
END = 300 #最后多少秒无视

def arg():
     "解析命令行引数:返回(文件名str,图片数int)的tuple"
     if len(sys.argv) == 3:
         filename = sys.argv[1].replace(' ','/ ')
         amount = sys.argv[2]
         if amount == '0':
             print 'You can not get 0 image !'
             exit()
         return (filename,int(amount))
     elif sys.argv[1] == '--help':
         print ''' *使用说明*
        previewmove 视频文件名 截取的图片数量
              '''
         exit()
     else:
         print 'Error argvs !'
         exit()

def getlength(filename):
     "用mplayer得到所给影片的长度(秒):返回长度的int"
     cmd = "mplayer -identify -nosound -vc dummy -vo null " + filename
     info = os.popen(cmd).readlines()
     length = filter(lambda str: str.find('ID_LENGTH') <> -1, info)[0].split('=')[1].split('.')[0]
     return int(length)

def getpoints(amount,length):
     "通过影片和所需图片数量计算出截取点:返回各点位置的iter,成员为int"
     space = ((length - END) - FRONT) / amount - 1
     points = (FRONT + (i-1) * space for i in range(1,amount+1))
     return points

def getimages(filename,points):
     "通过影片名与各点位置获取图片:返回图片文件名的tuple,成员为(文件名,截取点的时间)的tuple"
     points = list(points)
     images = []
     for i in points:

         cmd = "mplayer -ss " + str(i) + " -noframedrop -nosound -vo png -frames 1 " + filename
         os.system(cmd)
         toname = filename+'-'+ '1'*(points.index(i)+1) +'.png'
         os.rename('00000001.png',toname)
         images.append((toname,i))
     return tuple(images)

def extra(images,filename):
     "格式化那些图片"
     # 把时间加到每张图右下
     for i in images:
         cmd = "convert " +i[0] + " -fill black -undercolor white -pointsize 40 -gravity SouthEast -annotate +5+5 " + calctime(i[1]) + ' ' + i[0]+'.jpg'
         os.system(cmd)
     # 拼在一起
     cmd = "montage " + filename + "-*.png.jpg -resize 290 -geometry +5+5 -tile 2x  montage.jpg"
     os.system(cmd)
     # 改下名字吧
     os.rename('montage.jpg',filename.split('.')[0]+'.jpg')
     print filename.split('.')[0]+'.jpg', 'got !'
     return True

def extra2(images,filename):
      cmd = "montage " + filename + "-*.png-thumbnail 300x300 -bordercolor Lavender -background black +polaroid-resize 60%  -background lightGray -shadow -geometry -40+10 -tile x2overlap.jpg "
      os.system(cmd)
      return True

def extra3(images,filename):
       cmd = "convert " + filename + "-*.png -resize 200x200 -set delay 50 -loop 0 anim.gif"
       os.system(cmd)

def clear(images):
     "把不要文件清理了"
     for i in images:
         os.remove(i[0])
         os.remove(i[0]+'.jpg')
     return True

def calctime(sec):
     "把秒换算成小时xxxx --> xx:xx:xx,返回str"
     hour = sec/3600
     min = (sec - hour * 3600) / 60
     secd = sec - hour * 3600 - min * 60
     return '%02d:%02d:%02d' % (hour,min,secd)

def main():
     "主要执行逻辑"
     filename,amount = arg()
     length = getlength(filename)
     points = getpoints(amount,length)
     images = getimages(filename,points)
     extra(images,filename)
#        extra2(images,filename)
#        extra3(images,filename)
     clear(images)

main()