2

I've been stuck for weeks trying to use ffmpeg to convert user uploaded videos to flv. I use heroku to host my website, and store my static and media files on amazon S3 with s3boto. The initial video file will upload fine, however when I retrieve the video and run a celery task (in the same view where the initial video file is uploaded), the new file won't store on S3. I've been trying to get this to work for over a month, with no luck, and really no good resources available for learning how to do this, so I figure maybe if I can get the ffmpeg task to run before storing the video I may be able to get it to work. Unfortunately I'm still not a very advanced at python (or django), so I don't even know if/how this is possible. Anyone have any ideas? I am willing to use any solution at this point no matter how ugly, as long as it successfully takes video uploads and converts to flv using ffmpeg, with the resulting file being stored on S3. It doesn't seem that my situation is very common, because no matter where I look, I cannot find a solution that explains what I should be trying to do. Therefore I will be very appreciative of any guidance. Thanks. My relevant code follows:

#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')

I can add any other info if needed to help me solve my problem.

4

4 回答 4

0

为了将 subprocess.call 与包含所有参数的字符串一起使用,您需要添加shell=True

ffmpegresult = subprocess.call(ffmpeg, shell=True)
...
videothumbnail = subprocess.call(grabimage, shell=True)
于 2014-04-24T15:28:11.730 回答
0

我已经对您的代码进行了一些修改,现在它正在运行。一些问题:

  • 我已经将任务的调用放在模型的保存方法中。如果你愿意,你可以把它放在视图中,就像你做的那样。
  • 我的解决方案是转码为临时文件,然后将其上传到 AWS S3。

这是代码:

from django.contrib.auth.models import User

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)
    video_flv = models.FileField(upload_to='flv', blank=True)
    #videothumbnail = models.CharField(max_length=100, null=True, blank=True)
    video_thumbnail = models.FileField(upload_to='thumbs', blank=True)

    def __unicode__(self):
        return u'%s - %s' % (self.title, self.teacher)

    def save(self, *args, **kwargs):
        # optional parameter to indicate whether perform
        # conversion or not. Defaults to True
        do_conversion = kwargs.pop('do_conversion', True)

        # do something only when the entry is created
        if not self.pk:
            super(BroadcastUpload, self).save(*args, **kwargs)

        # do something every time the entry is updated
        if do_conversion:
            ffmpeg_image.delay(self.pk)
            convert_flv.delay(self.pk)

        # then call the parent save:
        super(BroadcastUpload, self).save(*args, **kwargs)

from django.core.files.uploadedfile import SimpleUploadedFile
import tempfile

@task
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)
    sourcefile = video.video_upload.url
    # ffmpeg cannot deal with https?
    sourcefile = sourcefile.replace("https","http")
    print "sourcefile: %s" % sourcefile

    # temporary output image
    OUTPUT_VIDEO_EXT = 'flv'
    OUTPUT_VIDEO_CONTENT_TYPE = 'video/flv'    
    f_out = tempfile.NamedTemporaryFile(suffix=".%s"%OUTPUT_VIDEO_EXT, delete=False)
    tmp_output_video = f_out.name

    #ffmpeg = "ffmpeg -i '%s' -qscale 0 -ar 44100 '%s'" % (sourcefile, vidfilename)
    ffmpeg = "ffmpeg -y -i '%s' -qscale 0 -ar 44100 '%s'" % (sourcefile, tmp_output_video)
    print "convert_flv: %s" % ffmpeg
    try:
        ffmpegresult = subprocess.call(ffmpeg, shell=True)
        #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("Failed to convert video file %s to %s" % (sourcefile, tmp_output_video))
        #print(traceback.format_exc())
        print "Error: %s" % e

    #vidfilename = "%s_%s.flv" % (teacher, video.id)
    vidfilename = "%s_%s.%s" % (teacher, video.id, OUTPUT_VIDEO_EXT)
    #targetfile = "%svideos/flv/%s" % (settings.MEDIA_URL, vidfilename)

    # prepare an object with the generated temporary image
    suf = SimpleUploadedFile( 
                             vidfilename,
                             f_out.read(),
                             content_type=OUTPUT_VIDEO_CONTENT_TYPE
                             )

    # upload converted video to S3 and set the name.
    # save set to False to avoid infinite loop
    video.video_flv.save(
                                vidfilename,
                                suf,
                                save=False 
                                )

    # delete temporary output file
    print "[convert_flv] removing temporary file: %s" % tmp_output_video
    os.remove(tmp_output_video)

    #video.flvfilename = vidfilename

    # add do_conversion=False to avoid infinite loop.
    # update_fields is needed in order to not delete video_thumbnail
    # if it did not exist when starting the task
    video.save(do_conversion=False, update_fields=['video_flv'])

@task       
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)
    sourcefile = video.video_upload.url
    # ffmpeg cannot deal with https?
    sourcefile = sourcefile.replace("https","http")

    # temporary output image
    OUTPUT_IMAGE_EXT = 'png'
    OUTPUT_IMAGE_CONTENT_TYPE = 'image/png'    
    f_out = tempfile.NamedTemporaryFile(suffix=".%s"%OUTPUT_IMAGE_EXT, delete=False)
    tmp_output_image = f_out.name

    #grabimage = "ffmpeg -y -i '%s' -vframes 1 -ss 00:00:02 -an -vcodec png -f rawvideo -s 320x240 '%s'" % (sourcefile, thumbnailfilename)
    grabimage = "ffmpeg -y -i '%s' -vframes 1 -ss 00:00:02 -an -vcodec png -f rawvideo -s 320x240 '%s'" % (sourcefile, tmp_output_image)
    print "ffmpeg_image: %s" % grabimage
    try:        
         videothumbnail = subprocess.call(grabimage, shell=True)
         #also tried separately following line:
         #videothumbnail = commands.getoutput(grabimage)
         print "---------------IMAGE---------------"
         print "VIDEOTHUMBNAIL: %s" % videothumbnail
    except Exception as e:
         videothumbnail = None
         #print("Failed to extract thumbnail from %s to %s" % (sourcefile, thumbnailfilename))
         print("Failed to extract thumbnail from %s to %s" % (sourcefile, tmp_output_image))
         #print(traceback.format_exc())
         print "Error: %s" % e

    #imagefilename = "%s_%s.png" % (teacher, video.id)
    imagefilename = "%s_%s.%s" % (teacher, video.id, OUTPUT_IMAGE_EXT)
    #thumbnailfilename = "%svideos/flv/%s" % (settings.MEDIA_URL, thumbnailfilename)
    #thumbnailfilename = 'thumbnail_image.png'

    # prepare an object with the generated temporary image
    suf = SimpleUploadedFile( 
                             imagefilename,
                             f_out.read(),
                             content_type=OUTPUT_IMAGE_CONTENT_TYPE
                             )

    # upload converted image to S3 and set the name.
    # save set to False to avoid infinite loop

    video.video_thumbnail.save(
                                imagefilename,
                                suf,
                                save=False 
                                )

    # delete temporary output file
    print "[ffmpeg_image] removing temporary file: %s" % tmp_output_image
    os.remove(tmp_output_image)

    #video.videothumbnail = imagefilename

    # add do_conversion=False to avoid infinite loop
    video.save(do_conversion=False, update_fields=['video_thumbnail'])
于 2014-05-03T10:24:21.630 回答
0

嘿,你犯了一个简单的错误

而不是使用 settings.Media_url 使用 media_root

于 2013-09-03T20:00:21.807 回答
0

不确定您是否会从类似的云场景中获得帮助,该场景涉及上传到云(parse.com)的媒体文件,这些文件在到达时需要 ffmpeg 进程,并将输出(.mp4)写回云(通过 Curl 解析)。

请参阅当前在 Heroku WEB 进程中运行的粘贴 shellscript,该进程可以使用 CLI 调用脚本。

如果您可以对其进行调整,以便 shellscript 在某个进程中运行,该进程具有对输入的 http 访问权限和一个可以写入临时文件的文件系统,并且如果您可以 CURL -X POST 来自 tmp FS 的 ffmpeg.output 回 S3,那么它可能对你有用。

于 2013-02-09T00:19:08.853 回答