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
新聞熱點
疑難解答