首頁 > Python, 程式設計 > 影片轉檔–Zencoder帶來的舒爽

影片轉檔–Zencoder帶來的舒爽

2012年7月2日 發表評論 閱讀評論

以前網站在做線上影片時,很辛苦的自己用ffmpeg做影片轉檔伺服器,不但常有影片會卡住沒轉到,更會轉出品質不是很理想的flv或mp4。曾想過用第三方轉檔平台處理轉檔的部份,但當時成本費用太高,而一直沒有把這一個部份做改變。
總算,現在在各方技術成熟的情況之下,不少線上多媒體轉檔平台變多了,在公司的團隊試用之下,最後決定使用 zencoder 來做為網站影音轉檔的平台。

zencoder


zencoder job list

zencoder job detail

zencoder job fail detail
Zencoder的優點講最實在的就是速度快、品質好,若以programer的角度來看,更是要有好用的API!這不是在幫Zencoder做廣告,而是他真的好用呀 XD!

而且費用也不是很貴(比較下來啦~要看公司對CP值的角度了 XD)

zencoder price

先來看一下使用者流程:

使用者流程

網站使用python搭配django,使用的套件就用 https://github.com/schworer/zencoder-py,他包裝了使用Zencoder的API需要的功能,我們只要把api key帶進去,就可以方便的把request(對zencoder提出轉檔需求)包起來送到Zencoder,也可以方便的取得每一個job(Zencoder處理轉檔的工作)的結果。來看一下實務上的範例吧!!

安裝必要的package:
[ccc lang="bash"]
pip insatll -e git://github.com/rdandy/zencoder-py.git#egg=zencoder-py
[/ccc]

在settings.py裡設定API KEY:
[ccc lang="python"]
ZENCODER_API_KEY = ‘這裡填上你的key’
ZENCODER_NOTIFICATION_ENDPOINT = ‘這裡填上zencoder要傳送轉檔結果的網址’
[/ccc]

設定zencoder的state代碼,參照document Get Job Details

Job states include pending, waiting, processing, finished, failed, and cancelled.

Input states include pending, waiting, processing, finished, failed, and cancelled.

Output states include waiting, queued, assigning, processing, finished, failed, cancelled and no input.

我們可以設定這樣,
[ccc lang="python"]
# 除了Zencoder的狀態之外,我們也要對自己本身的狀態做識別
# 0 : 使用者上傳中,影片還未儲存完畢
# 1 : 使用者上傳完畢,可以準備送到zencoder
JOB_STATE = (
(0, ‘nosourcesaved’),
(1, ‘readytoencoder’),
(2, ‘waiting’),
(3, ‘uploading’),
(4, ‘uploaded’),
(5, ‘created’),
(6, ‘processing’),
(7, ‘finished’),
(8, ‘failed’),
(9, ‘cancelled’),
)

OUTPUT_STATE = (
(0, ‘waiting’),
(1, ‘queued’),
(2, ‘assigning’),
(3, ‘processing’),
(4, ‘finished’),
(5, ‘failed’),
(6, ‘cancelled’),
(7, ‘no_input’),
)
[/ccc]

以上算是對使用zencoder的基本設定,接著簡短的來看zencoder-py要怎麼使用。

[ccc lang="python"]
from zencoder import Zencoder
zen = Zencoder(‘your_api_key’)
[/ccc]

這個zen物件就可以用來傳送和取得zencoder的api了

送出request:

[ccc lang="python"]
def get_random_prefix():
return str(uuid.uuid4()).replace(‘-‘, “)

def zencode(video):
from video.models import VideoOutput, VideoJob

source_url = video.source_path.url
print source_url

prefix = get_random_prefix()
ext = ‘.mp4’

username = video.user.username
level1 = username[0:2]
level2 = username[2:4]

output_base_level = ‘video/%s/%s/%s’ % (level1, level2, username)
output_base_url = ‘s3://%s/%s’ % (settings.AWS_STORAGE_BUCKET_NAME, output_base_level)
output_filename = ‘VIDEO_%s_%s%s’ % (video.id, prefix, ext)
output_url = ‘%s/%s’ % (output_base_url, output_filename)

output_capture_base_level = ‘video_capture/%s/%s/%s’ % (level1, level2, username)
output_capture_base_url = ‘s3://%s/%s’ % (settings.VIDEO_BUCKET_NAME, output_capture_base_level)
output_capture_filename = ‘VIDEO_%s_%s_{{number}}’ % (video.id, prefix)

bucket_url = ‘http://%s.s3.amazonaws.com/’ % settings.VIDEO_BUCKET_NAME

# creates an encoding job with the defaults

thumbnails = {
‘format’: ‘jpg’,
‘size’: ‘720×480’,
‘aspect_mode’: ‘pad’,
‘number’: 10,
‘public’: 1,
‘base_url’: output_capture_base_url,
‘filename’: output_capture_filename,
}

options = {‘region’: ‘asia-tokyo’}

res480p = {
‘base_url’: output_base_url,
‘filename’: output_filename,
‘url’: ‘指定輸出檔的path’,
‘format’: ‘mp4’,
‘video_codec’: ‘h264’,
‘audio_codec’: ‘aac’,
‘size’: ‘720×480’,
‘aspect_mode’: ‘pad’,
‘audio_sample_rate’: 44100,
‘audio_channels’: 2,
‘audio_bitrate’: 96,
‘label’: ‘480p’,
‘speed’: 2,
‘public’: 1,
‘thumbnails’: thumbnails,
‘notifications’: None,
‘headers’: {
‘Expires’: ‘Thu, 31 Dec 2020 23:59:59 GMT’,
‘Cache-Control’: ‘max-age=99999’
},
}

res480p[‘notifications’] = settings.ZENCODER_NOTIFICATION

outputs = (res480p, )

#送出 request
job = zen.job.create(source_url, outputs=outputs, options=options)

if job.code == 201:
"""
對影片記錄的工作在這裡處理
"""
else:
"""
錯誤處理
"""
[/ccc]

送出的request的json大致上是這個格式:
[ccc lang="text"]
{
"test": 0,
"input": "http://path/to./your/video/source/path/filename.mp4",
"region": "asia-tokyo",
"api_key": "your_api_key",
"outputs": [{
"audio_channels": 2,
"thumbnails": {
"format": "jpg",
"base_url": "s3://yourbucket/path/to/image/path",
"number": 10,
"aspect_mode": "pad",
"filename": "VIDEO_33269_0d5ea93c6bac40f0a8b1350da403498e_{{number}}",
"public": 1,
"size": "720×480"
},
"format": "mp4",
"url": "s3://bucketname/path/to/your/video/path/filename.mp4",
"audio_bitrate": 96,
"base_url": "s3://bucketname/path/to/your/video/path",
"filename": "filename.mp4",
"headers": {
"Expires": "Thu, 31 Dec 2020 23:59:59 GMT",
"Cache-Control": "max-age=99999"
},
"public": 1,
"aspect_mode": "pad",
"audio_codec": "aac",
"video_codec": "h264",
"label": "480p",
"speed": 2,
"audio_sample_rate": 44100,
"notifications": [{
"url": "http://yourdomain/api/notification/",
"format": "json"
},
"your@notificationameil"],
"size": "720×480"
}]
}
[/ccc]

接收zencoder轉檔結果的notification

[ccc lang="python"]
from django.conf import settings
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt
from datetime import datetime
from video.utils import get_job_state_id, get_output_state_id
from django.utils import simplejson

@csrf_exempt
@require_POST
def notification(request):

loaddata = request.raw_post_data
result = simplejson.loads(loaddata)

output_id = result[‘output’][‘id’]

job_id = result[‘job’][‘id’]
job_state = result[‘job’][‘state’]
job_state_id = get_job_state_id(job_state)

output_url = "
if result[‘output’][‘url’]:
output_url = ‘/’.join(result[‘output’][‘url’].split(‘/’)[3:])

output_state = 0
if result[‘output’][‘state’]:
output_state = get_output_state_id(result[‘output’][‘state’])

# update video output state
try:
output = VideoOutput.objects.get(output_id=output_id)
output.state = output_state
output.file = output_url
output.width = result[‘output’][‘width’] or 0
output.height = result[‘output’][‘height’] or 0
output.save()
except VideoOutput.DoesNotExist:
output = None
pass

send_to_user_email = False

# update video state
try:
video = Video.objects.get(job_id=job_id)

# video is pass
if job_state_id == 7 and output_state == 4:
secs = float(result[‘output’][‘duration_in_ms’]) / 1000
secs = int(math.ceil(secs))

video.file = output_url
video.secs = secs
video.file_size = result[‘output’][‘file_size_in_bytes’]
video.save()

# thumbnails
images = []
try:
thumbnails = result[‘output’][‘thumbnails’][0]
except KeyError:
thumbnails = None

if thumbnails:
images = thumbnails[‘images’]

if len(images) > 0:
VideoCaptures.objects.filter(video=video).delete()

#image_path = ‘/’.join(images[len(images) – 1][‘url’].split(‘/’)[3:])
image_path = ‘/’.join(images[0][‘url’].split(‘/’)[3:])

# check job counts, if a video has more then 1 job, no send mail and create timeline
if video.jobs.all().count() == 1:
send_to_user_email = True

for image in images:
#images order in json is 9 to 0

VideoCaptures(
video = video,
created_at = datetime.now(),
image = ‘/’.join(image[‘url’].split(‘/’)[3:])
).save()

#將更新source info的工作交給tasks
tasks.update_video_source_info(video.id)

# encode failed or canceled
if job_state_id in (8, 9):
# 不成功的job在這裡處理,這邊先pass吧
pass

# update video job state
job = VideoJob.objects.get(id=job_id)
job.state = job_state_id
job.save()

video.update_state()

except Video.DoesNotExist:
pass

return HttpResponse(‘done’)
[/ccc]

如此,可以將原本從自行維護及管理一台轉檔機的工作,交由第三方處理,我們只需要做好影片接收、送出request、接收轉好的檔案等三個工作,就可以給user更好品質的線上影片了!

  1. 目前尚無任何的評論。