Stickyworld 的网页应用已经支持视频拨放一段时间,但都是通过YouTube的嵌入模式实现。我们开始提供新的版本支持视频操作,可以让我们的用户不用受制于YouTube的服务。
我过去曾经参与过一个项目,客户需要视频转码功能,这实在不是个容易达成的需求。需要大量的读取每一个视频、音讯与视频容器的格式再输出符合网页使用与喜好的视频格式。
考虑到这一点,我们决定将转码的工作交给 Encoding.com 。这个网站可以免费让你编码1GB大小的视频,超过1GB容量的文件将采取分级计价收费。
开发的代码如下,我上传了一个178KB容量的两秒视频来测试代码是否成功运作。当测试过程没有发生任何的例外错误后,我继续测试其它更大的外部文件。
阶段一:用户上传视频文件
现在这的新的代码段提供了一个基于 HTML5且可以快速上手的 的上传机制。用CoffeeScript撰写的代码,可以从客户端上传文件到服务器端。
$scope.upload_slide = (upload_slide_form) -> file = document.getElementById("slide_file").files[0] reader = new FileReader() reader.readAsDataURL file reader.onload = (event) -> result = event.target.result fileName = document.getElementById("slide_file").files[0].name $.post "/world/upload_slide", data: result name: fileName room_id: $scope.room.id (response_data) -> if response_data.success? is not yes console.error "There was an error uploading the file", response_data else console.log "Upload successful", response_data reader.onloadstart = -> console.log "onloadstart" reader.onprogress = (event) -> console.log "onprogress", event.total, event.loaded, (event.loaded / event.total) * 100 reader.onabort = -> console.log "onabort" reader.onerror = -> console.log "onerror" reader.onloadend = (event) -> console.log "onloadend", event
最好可以通过 (“slide_file”).files 且经由独立的POST上传每个文件,而不是由一个POST需求上传所有文件。稍后我们会解释这点。
阶段二:验证并上传至 Amazon S3
后端我们运行了Django与RabbitMQ。主要的模块如下:
$ pip install 'Django>=1.5.2' 'django-celery>=3.0.21' / 'django-storages>=1.1.8' 'lxml>=3.2.3' 'python-magic>=0.4.3'我建立了两个模块:SlideUploadQueue 用来储存每一次上传的数据,SlideVideoMedia 则是用来储存每个要上传影片的数据。 class SlideUploadQueue(models.Model): created_by = models.ForeignKey(User) created_time = models.DateTimeField(db_index=True) original_file = models.FileField( upload_to=filename_sanitiser, blank=True, default='') media_type = models.ForeignKey(MediaType) encoding_com_tracking_code = models.CharField( default='', max_length=24, blank=True) STATUS_AWAITING_DATA = 0 STATUS_AWAITING_PROCESSING = 1 STATUS_PROCESSING = 2 STATUS_AWAITING_3RD_PARTY_PROCESSING = 5 STATUS_FINISHED = 3 STATUS_FAILED = 4 STATUS_LIST = ( (STATUS_AWAITING_DATA, 'Awaiting Data'), (STATUS_AWAITING_PROCESSING, 'Awaiting processing'), (STATUS_PROCESSING, 'Processing'), (STATUS_AWAITING_3RD_PARTY_PROCESSING, 'Awaiting 3rd-party processing'), (STATUS_FINISHED, 'Finished'), (STATUS_FAILED, 'Failed'), ) status = models.PositiveSmallIntegerField( default=STATUS_AWAITING_DATA, choices=STATUS_LIST) class Meta: verbose_name = 'Slide' verbose_name_plural = 'Slide upload queue' def save(self, *args, **kwargs): if not self.created_time: self.created_time = / datetime.utcnow().replace(tzinfo=pytz.utc) return super(SlideUploadQueue, self).save(*args, **kwargs) def __unicode__(self): if self.id is None: return 'new <SlideUploadQueue>' return '<SlideUploadQueue> %d' % self.id class SlideVideoMedia(models.Model): converted_file = models.FileField( upload_to=filename_sanitiser, blank=True, default='') FORMAT_MP4 = 0 FORMAT_WEBM = 1 FORMAT_OGG = 2 FORMAT_FL9 = 3 FORMAT_THUMB = 4 supported_formats = ( (FORMAT_MP4, 'MPEG 4'), (FORMAT_WEBM, 'WebM'), (FORMAT_OGG, 'OGG'), (FORMAT_FL9, 'Flash 9 Video'), (FORMAT_THUMB, 'Thumbnail'), ) mime_types = ( (FORMAT_MP4, 'video/mp4'), (FORMAT_WEBM, 'video/webm'), (FORMAT_OGG, 'video/ogg'), (FORMAT_FL9, 'video/mp4'), (FORMAT_THUMB, 'image/jpeg'), ) format = models.PositiveSmallIntegerField( default=FORMAT_MP4, choices=supported_formats) class Meta: verbose_name = 'Slide video' verbose_name_plural = 'Slide videos' def __unicode__(self): if self.id is None: return 'new <SlideVideoMedia>' return '<SlideVideoMedia> %d' % self.id
新闻热点
疑难解答