我能在存储视频前使用ffmpeg将Django表单上传的视频转换吗?
我已经卡了好几个星期,想用ffmpeg把用户上传的视频转换成flv格式。我在heroku上托管我的网站,并把静态文件和媒体文件存储在亚马逊S3上,使用的是s3boto。最开始的视频文件上传得很顺利,但是当我取回视频并在同一个视图中运行celery任务时,新文件却没有存储到S3上。我已经尝试了一个多月都没有成功,而且找不到什么好的资源来学习怎么做,所以我想如果能在存储视频之前先运行ffmpeg任务,或许能解决问题。不幸的是,我在python(或django)方面还不是很熟练,所以我甚至不知道这是否可行。有没有人有好的主意?现在我愿意尝试任何解决方案,只要能成功处理视频上传并用ffmpeg转换成flv格式,最后把生成的文件存储到S3上。我的情况似乎不太常见,因为无论我在哪里查找,都找不到解释我该怎么做的解决方案。因此,我会非常感激任何指导。谢谢。以下是我相关的代码:
#models.py
def content_file_name(instance, filename):
ext = filename.split('.')[-1]
new_file_name = "remove%s.%s" % (uuid.uuid4(), ext)
return '/'.join(['videos', instance.teacher.username, new_file_name])
class BroadcastUpload(models.Model):
title = models.CharField(max_length=50, verbose_name=_('Title'))
description = models.TextField(max_length=100, verbose_name=_('Description'))
teacher = models.ForeignKey(User, null=True, blank=True, related_name='teacher')
created_date = models.DateTimeField(auto_now_add=True)
video_upload = models.FileField(upload_to=content_file_name)
flvfilename = models.CharField(max_length=100, null=True, blank=True)
videothumbnail = models.CharField(max_length=100, null=True, blank=True)
#tasks.py
@task(name='celeryfiles.tasks.convert_flv')
def convert_flv(video_id):
video = BroadcastUpload.objects.get(pk=video_id)
print "ID: %s" % video.id
id = video.id
print "VIDEO NAME: %s" % video.video_upload.name
teacher = video.teacher
print "TEACHER: %s" % teacher
filename = video.video_upload
sourcefile = "%s%s" % (settings.MEDIA_URL, filename)
vidfilename = "%s_%s.flv" % (teacher, video.id)
targetfile = "%svideos/flv/%s" % (settings.MEDIA_URL, vidfilename)
ffmpeg = "ffmpeg -i %s %s" % (sourcefile, vidfilename)
try:
ffmpegresult = subprocess.call(ffmpeg)
#also tried separately with following line:
#ffmpegresult = commands.getoutput(ffmpeg)
print "---------------FFMPEG---------------"
print "FFMPEGRESULT: %s" % ffmpegresult
except Exception as e:
ffmpegresult = None
print("Failed to convert video file %s to %s" % (sourcefile, targetfile))
print(traceback.format_exc())
video.flvfilename = vidfilename
video.save()
@task(name='celeryfiles.tasks.ffmpeg_image')
def ffmpeg_image(video_id):
video = BroadcastUpload.objects.get(pk=video_id)
print "ID: %s" %video.id
id = video.id
print "VIDEO NAME: %s" % video.video_upload.name
teacher = video.teacher
print "TEACHER: %s" % teacher
filename = video.video_upload
sourcefile = "%s%s" % (settings.MEDIA_URL, filename)
imagefilename = "%s_%s.png" % (teacher, video.id)
thumbnailfilename = "%svideos/flv/%s" % (settings.MEDIA_URL, thumbnailfilename)
grabimage = "ffmpeg -y -i %s -vframes 1 -ss 00:00:02 -an -vcodec png -f rawvideo -s 320x240 %s" % (sourcefile, thumbnailfilename)
try:
videothumbnail = subprocess.call(grabimage)
#also tried separately following line:
#videothumbnail = commands.getoutput(grabimage)
print "---------------IMAGE---------------"
print "VIDEOTHUMBNAIL: %s" % videothumbnail
except Exception as e:
videothumbnail = None
print("Failed to convert video file %s to %s" % (sourcefile, thumbnailfilename))
print(traceback.format_exc())
video.videothumbnail = imagefilename
video.save()
#views.py
def upload_broadcast(request):
if request.method == 'POST':
form = BroadcastUploadForm(request.POST, request.FILES)
if form.is_valid():
upload=form.save()
video_id = upload.id
image_grab = ffmpeg_image.delay(video_id)
video_conversion = convert_flv.delay(video_id)
return HttpResponseRedirect('/current_classes/')
else:
form = BroadcastUploadForm(initial={'teacher': request.user,})
return render_to_response('videos/create_video.html', {'form': form,}, context_instance=RequestContext(request))
#settings.py
DEFAULT_FILE_STORAGE = 'myapp.s3utils.MediaRootS3BotoStorage'
DEFAULT_S3_PATH = "media"
STATICFILES_STORAGE = 'myapp.s3utils.StaticRootS3BotoStorage'
STATIC_S3_PATH = "static"
AWS_STORAGE_BUCKET_NAME = 'my_bucket'
CLOUDFRONT_DOMAIN = 'domain.cloudfront.net'
AWS_ACCESS_KEY_ID = 'MY_KEY_ID'
AWS_SECRET_ACCESS_KEY = 'MY_SECRET_KEY'
MEDIA_ROOT = '/%s/' % DEFAULT_S3_PATH
MEDIA_URL = 'http://%s/%s/' % (CLOUDFRONT_DOMAIN, DEFAULT_S3_PATH)
...
#s3utils.py
from storages.backends.s3boto import S3BotoStorage
from django.utils.functional import SimpleLazyObject
StaticRootS3BotoStorage = lambda: S3BotoStorage(location='static')
MediaRootS3BotoStorage = lambda: S3BotoStorage(location='media')
如果需要,我可以提供其他信息来帮助我解决问题。
4 个回答
为了使用subprocess.call这个功能,并且传入一个包含所有参数的字符串,你需要加上 shell=True 这个选项:
ffmpegresult = subprocess.call(ffmpeg, shell=True)
...
videothumbnail = subprocess.call(grabimage, shell=True)
嘿,你犯了个简单的错误。
你应该用 media_root,而不是 settings.Media_url。
不太确定你是否能从一个类似的云场景中得到帮助,这个场景涉及到上传到云端(parse.com)的媒体文件,这些文件在到达时需要用ffmpeg处理,然后将输出(.mp4格式)写回云端(通过Curl)。
可以看看这个shell脚本,它目前在Heroku的WEB进程中运行,可以通过命令行调用其他脚本。
如果你能调整这个脚本,让它在某个可以访问HTTP的进程中运行,并且有一个文件系统可以写入临时文件,同时你能通过CURL -X POST将ffmpeg的输出从临时文件系统发送回S3,那么这可能对你有帮助。