分类 编程语言 下的文章

      一个很常见的业务场景,我们有很多的MP4视频文件,在列表页中,是需要展示一个图片作为封面。在此之前,我们的封面图片都是jpg。然鹅,某天产品汪心血来潮,想要把gif动画来作为封面,那么问题就来了,如何把MP4的几秒钟内容提出来生成一个gif动画呢?比如每个MP4视频文件的第10秒到15秒这5秒钟的内容转成gif呢?


      大的思路上,MP4是一个视频,视频是由帧组成的,一帧是一张图片,比如一秒播放60张,连起来就是个视频。


      先上最关键的命令,ffmpeg提供了将MP4转gif的操作:

ffmpeg -ss 25 -t 5 -i shipin.mp4 -r 5 -s 150x150 -y -f gif shipin.gif


-ss 25 -t 5:从视频25开始,一共5秒。

-i shipin.mp4:输入的视频

-r 5: 一秒取5帧。

-s 150x150:生成的图片是150*150尺寸。

-y 同名覆盖

-f gif:输出gif格式。


方法一:

MP4是由客户端APP直接上传到阿里云的OSS存储服务中,那么阿里云是否由接口,这样最简单方便可以打发走PM。调研发现,阿里云提供了MP4转GIF服务,实测发现,这个服务不能只转视频的一部分,它会把整个视频文件都给转了,比如38M的MP4文件会转成一个158M的GIF。这种方式PASS掉。


方法二:

在方法一的基础上,咨询阿里云的客服,给出的方案是,阿里云提供MP4指定秒数的一帧提出来生成一张jpg,然后多张jpg生成一张GIF。这样的话,阿里云生成的多个jpg要自己下载下来用生成一个gif。


方法三:

使用ffmpeg自己把MP4转gif。

ffmpeg提供了一个功能,可以把MP4中,指定秒数开始 + 一共多少秒 + 一秒取多少帧 + 图片多少*多少。


最佳时间:


以服务的形式,对外提供MP4生成GIF的服务。生成完成后,再以http回调的方式告诉上层应用,我这边生成好了。


具体步骤:


1、上层应用会在半夜开始跑脚本,根据各项指标,计算出几万个上首页的视频。

2、上层应用调我的service代码,传video_id给我。

3、我拿video_id查库得出视频在阿里云的http地址。

4、计算token,并将video_id + token存在Memcache里,过期时间7200秒。

5、以http的方式调MP4转GIF的服务。将video_id + token + 视频url + 回调函数的http地址。

6、在服务层,收到了请求,校验参数和token的合法性,执行一个ffmpeg命令

Ps:第5步和第6步可以用消息队列来解耦,太麻烦了。

7、生成完成后,传到阿里云的CDN上去,再以回调函数的形式,告诉上层应用,我已经做完了。

8、给上层应用service代码中,验证一下回调参数的video_id和token是不是之前存好的。

9、最后就是,上层应用修改数据库里的字段。


问题:不用消息队列的话,就面临着每次请求时一个HTTP,请求方会一直等着响应方回话。

解决:这个时候,我们可以在请求方的curl中设置超时时间,比如1秒。在响应方,让程序继续往下走,不要因为连接断开而终止执行。毕竟,请求方不需要等待响应,而是用回调方式的告知结果。


代码很简单,不放了。

Golang的Switch与Select的逻辑,和其他语言不同。一不留神就是个坑。


多个Case中时,第一个case总是被抛弃的,同时也不会进到default里面。


如下的例子:

a := 2

switch a {

case 2:

case 3:

    fmt.Println("第一个case")

case 4, 5:

    fmt.Println("第二个case")

default:

    fmt.Println("Default")

}


当a=2时,没有任何输出。无论第一个打印,还是default都没有。

当a=3时,输出“第一个case”。

当a=4时,输出“第二个case”。

当a=5时,输出“第二个case”。

当a=其他值时,才输出default。


和switch一样,select是相同的逻辑和分支走向。


c1 := make(chan int, 10)

c2 := make(chan int, 10)

c1<-1

select{

case <-c1:

case <-c2:

    fmt.Println("进来了")

default:

    fmt.Println("default")

}

此时虽然c1有值,但是c1在select中是被抛弃的,所以无任何输出。

把c1<-1改成c2<-1,输出“进来了”。

当c1和c2都没有值,才会进入到default中。

另外,switch可以case1,2这样逗号分隔,同一行的case中写多个值,这种写法在select中是不行的。

    最近在学习Golang,看资料过程中收集了一部分Go入门资源。供大家浏览,也当自己收藏。包括Go语言的安装和配置,Go入门,语法词法。还有一些函数库,包括net/http,time,buffer等。持续更新


1、安装和入门,基础语法:https://blog.linguofeng.com/pages/language/go.html


2、Go指针详解:http://my.oschina.net/u/943306/blog/131269


3、time包:http://my.oschina.net/u/943306/blog/149395


4、bytes/buffer包:http://my.oschina.net/u/943306/blog/127981


5、net/http包:http://my.oschina.net/u/943306/blog/151293


6、Go Web编程(开源书籍,有PDF和EPUB和Gitbook):https://github.com/astaxie/build-web-application-with-golang





本例是Python基础示例。涉及Python基础,包括语法、字典型数据结构、类、引入库、pickle实现的存储器、异常处理等。
   示例是一个电话本。可以对电话本进行增加、删除、修改、获取列表和获取单人的。
   Python中,Pickle和cPickle都可以完成存储器的任务,不过cPickle是C语言所写,据称性能高于Pickle1000倍
   Python中的Pickle是把一个对象存入文件中。作为完全面向对象的语言,在声明/初始化一个变量的时候,比如字典,也就是关联数组,Python其实是在实例化一个字典对象。那么Pickle就可以把这个字典对象存入一个文件,读出来的时候不但这个字典是完整的数据,而且可以继续使用这个字典对象的方法。
   Python是用缩进来时别语句块的。因为我是在VIM下写好复制出来的,所以在博客看到的可能缩进会有问题。

#引入pickle库。cPickle比Pickle快1000倍
import cPickle as pickle
#import Pickle as pickle

#电话本类
class Address:
    #初始化
    def __init__(self):
        #把数据存到那个文件里
	    self.filename = 'list.data'
	    f = file(self.filename)
        #如果文件是新建的或者是空内容的,则初始化为一个空的字典(关联数组)
        try:
            self.lists = pickle.load(f)
        except:
            print 'Address Book is empty.initializing.....'
            self.lists = {}
	    f.close()
    #添加联系人
    def add(self, name, age, mobile, mail):
	    newUser = {'name':name, 'age':age, 'mobile':mobile, 'mail':mail}
	    self.lists[name] = newUser
    #删除联系人
    def delete(self, name):
        if name in self.lists:
            del self.lists[name]
            print 'delete ', name
        else:
            print 'No exists ', name
    #获取列表
    def getList(self):
        print 'Address Book List:'
        print self.lists
    #获取指定姓名的联系人
    def getOne(self, name):
        if name in self.lists:
            print self.lists[name]
        else:
            print 'Not Exists:', name
    #修改联系人
    def edit(self, name, key, value):
	    self.lists[name][key] = value
    #类运行结束,执行特殊方法__del__,也就是析构函数
    def __del__(self):
        f = file(self.filename, 'w')
        pickle.dump(self.lists, f)
        f.close()

#初始化电话本类
obj = Address()
#添加一个联系人
obj.add('lane', 23, 18500000000, 'lixuan868686@163.com')
#获取所有联系人的列表
obj.getList()
#获取lane这个人的联系方式
obj.getOne('lane')
#获取xiaoming这个人的联系方式
obj.getOne('xiaoming')
//修改lane这个人的年龄为24
obj.edit('lane', 'age', '24')