【问题标题】:Can I convert a django video upload from a form using ffmpeg before storing the video?在存储视频之前,我可以使用 ffmpeg 从表单转换 django 视频上传吗?
【发布时间】:2013-01-24 10:33:29
【问题描述】:

几周以来,我一直在尝试使用 ffmpeg 将用户上传的视频转换为 flv。我使用 heroku 来托管我的网站,并使用 s3boto 将我的静态和媒体文件存储在亚马逊 S3 上。初始视频文件可以正常上传,但是当我检索视频并运行 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')

如果需要,我可以添加任何其他信息来帮助我解决问题。

【问题讨论】:

    标签: python heroku amazon-s3 ffmpeg django-celery


    【解决方案1】:

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

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

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

    【讨论】:

    • 感谢您的回复。我会看看我是否可以使用帮助。
    【解决方案2】:

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

    而不是使用 settings.Media_url 使用 media_root

    【讨论】:

      【解决方案3】:

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

      ffmpegresult = subprocess.call(ffmpeg, shell=True)
      ...
      videothumbnail = subprocess.call(grabimage, shell=True)
      

      【讨论】:

        【解决方案4】:

        我已经对您的代码进行了一些修改,现在它可以工作了。 一些问题:

        • 我已将任务调用放在模型的保存方法中。 如果需要,您可以将其放入视图中。
        • 我的解决办法是转码为临时文件,然后上传 它到 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'])
        

        【讨论】:

          猜你喜欢
          • 2011-04-26
          • 2020-10-08
          • 1970-01-01
          • 2013-06-17
          • 2016-11-25
          • 2011-10-30
          • 2011-06-29
          • 1970-01-01
          • 2013-02-26
          相关资源
          最近更新 更多