FFMPEG命令行工具目前无法针对视频实时流进行转码,而我们可以用它的新版本提供的支持TCP SERVER的特性来实现这一功能。实现边录边用FFMPEG提供的命令行工具进行转码。(实践证明,FFMPEG 的SDK针对实时流开发比较繁琐,命令行工具相对来说稳定)
我们将实时流录制成正在增长的文件,这样问题就变成了FFMPEG如何针对正在增长的视频文件转码。(首先我们保证视频格式是可以支持正在增长文件解码,这不在本文的讨论范畴)
而我们如果直接使用它对正在增长的文件转码,我们会发现FFMPEG转码到文件末尾(转码速度比录制速度快)就会退出。我们要做的就是在FFMPEG转码到末尾时如何将其挂起。若直接修改FFMPEG的源代码则比较繁琐,我们搭建一个TCP的FILE SERVER,来控制给FFMPEG发送数据的速度,而FFMPEG使用TCP的方式来获取视频流,即可实现这个功能。
以下是代码,我们用python来实现:
FFMPEG version:N-32611-gd55b06b
- #encoding=utf8
- '''''
- Created on 2011-9-25
- @author: chenggong
- ffmpeg扩展工具
- '''
- import SocketServer
- import os
- import threading
- import time
- BIN_DIR = "bin\\"
- BLOCK_SIZE = 188*1024
- FRONTOFFSET = 1024 * 1024 * 1
- gparam={'output':'','source':'','port':9333,
- 'start':0,'length':0,'ffmpeg_argvs':'',
- 'ffmpegsema':None,'tcpserversema':None}
- class MyFfmpegThread(threading.Thread):
- def run(self):
- if os.path.exists(gparam['output']):
- os.remove(gparam['output'])
- argvs = gparam['ffmpeg_argvs'].replace("[filelocate]","tcp://127.0.0.1:%d"%gparam['port']) + " " + gparam['output']
- cmd = "%s\\avconv %s"%(BIN_DIR,argvs)
- os.system(cmd)
- gparam['ffmpegsema'].release()
- class MyTcpServerThread(threading.Thread):
- def run(self):
- while True:
- try:
- ADDR = ("127.0.0.1", gparam['port'])
- self.tcpServ = SocketServer.ThreadingTCPServer(ADDR, MyRequestHandler)
- break
- except Exception,e:
- print str(e)
- print "port:%d 被占用..更换"%gparam['port']
- gparam['port']+=1
- self.tcpServ.serve_forever()
- def close(self):
- self.tcpServ.shutdown()
- self.tcpServ.server_close()
- class MyRequestHandler(SocketServer.BaseRequestHandler):
- def handle(self):
- print 'connected from:', self.client_address
- file = open(gparam['source'],"rb")
- startoffset = gparam['start']
- if startoffset<0:startoffset=0
- print "start offset="+str(startoffset)
- file.seek(startoffset)
- left = gparam['length']
- while(True):
- try:
- if(left<BLOCK_SIZE and left!=-1):
- buffer_size = left
- else:
- buffer_size = BLOCK_SIZE
- data=file.read(buffer_size)
- #print "read data,size="+str(len(data))
- #if(left!=-1): print "left size=%d"%left
- if not data:
- print "data empty! read to file_end,wait.."
- finish_filename=gparam['source'].replace(os.path.splitext(gparam['source'])[1],".finish")
- if(os.path.exists(finish_filename)):
- print ".finish file founded"
- break
- time.sleep(1)
- continue
- self.request.sendall(data)
- if(left!=-1):
- left -= buffer_size
- if(left<=0):
- print "transfer finished"
- break
- if(len(data)<buffer_size):
- finish_filename=gparam['source'].replace(os.path.splitext(gparam['source'])[1],".finish")
- if(os.path.exists(finish_filename)):
- print ".finish file founded"
- break
- else:
- print "read to file_end,wait.."
- time.sleep(1)
- except:
- print "exception occured,client stopped"
- break
- print "work finished"
- file.close()
- gparam['tcpserversema'].release()
- def ffmpegex(source,output,start,length,ffmpeg_argvs):
- gparam['source']=source
- gparam['output']=output
- gparam['start']=start
- gparam['length']=length
- gparam['ffmpeg_argvs']=ffmpeg_argvs
- try:
- while True:
- gparam['ffmpegsema']=threading.Semaphore(0)
- gparam['tcpserversema']=threading.Semaphore(0)
- tcpServerThread = MyTcpServerThread()
- tcpServerThread.start()
- MyFfmpegThread().start()
- gparam['ffmpegsema'].acquire()
- gparam['tcpserversema'].acquire()
- tcpServerThread.close()
- filesize = os.path.getsize(gparam['output'])
- if(filesize<length/10): #生成失败,将start提前,重做任务
- if gparam['start']==0: return False
- if gparam['start']-FRONTOFFSET>=0:
- gparam['start']-=FRONTOFFSET
- else:
- gparam['start']=0
- else:
- return True
- except:
- return False
- if __name__ == "__main__":
- for i in range(0,100):
- import random
- start = random.randrange(0,1024*1024*1024)
- print ffmpegex('C:\\16942.ts','C:\\test2\\%d-%d.wmv'%(i,start),start,1024*1024*5,\
- "-i [filelocate] -acodec wmav2 -vcodec wmv2 -qscale 1 -ab 256k -r 25")
-