{"id":889,"date":"2012-09-12T14:58:14","date_gmt":"2012-09-12T12:58:14","guid":{"rendered":"http:\/\/www.alkannoide.com\/?p=889"},"modified":"2012-12-13T11:49:55","modified_gmt":"2012-12-13T10:49:55","slug":"microsoft-azure-media-services-how-to-encode-in-the-azure-cloud-part-2","status":"publish","type":"post","link":"https:\/\/www.alkannoide.com\/2012\/09\/12\/microsoft-azure-media-services-how-to-encode-in-the-azure-cloud-part-2\/","title":{"rendered":"Microsoft Azure Media Services \u2013 How to encode in the Azure Cloud \u2013 Part 2"},"content":{"rendered":"
After the first post which give an overview and explain how set-up a WAMS environment, I continue the discovery of WAMS World<\/a> (sorry for the pun ;-)). In this post, we will see how we can transform a video from a input format to another format. It’s possible to generate multiple output format or to encrypt the video. But we will see it in a next posts.<\/p>\n <\/p>\n First of all, we need to understand the different terms we will use in WAMS. In my example, I want to obtain this workflow :<\/p>\n As I said previously, the AMS is based on REST, so you use HTTP verbs to make an action. Here is the PHP class, I used to make API call (I completed the code given in previous post<\/a>):<\/p>\n You can look, the code manages the token, so you only need to make the API call.<\/p>\n Now, we will create an asset. To do that, we need<\/p>\n We will create an asset with POST verb<\/a>.<\/p>\n Ok, the server answers, the asset was created. Now, we will verified by listing the different asset. The method below will be added to the previous one.<\/p>\n Note the asset ID, you need it after.\u00a0Now, we call the list Asset API<\/a>.<\/p>\n Good, we have our first_sintel_test asset with his creation date, his status and some others properties<\/a>. You can filter to get informations about one asset by adding the ID (a unique identifier).<\/p>\n Now, we can upload our file in blob storage.<\/p>\n To upload a file and associate to an asset, you need to give access via policies<\/a> (time duration and permission: read, write, delete,…). You can create the policy, the process is the same like the Asset creation. Note the access ID, you need it after.<\/p>\n We have now a policy to upload the file, we have to get a URL path to upload by created a\u00a0Locator<\/a>. When it will be created, it will available in the Asset list API. To create a Locator, you need 4 informations :<\/p>\n Ok, now we can upload the file with\u00a0Windows Azure Storage Services REST API<\/a>. You can check that your video was uploaded correctly via the Azure Portal : Storage -> Select your storage -> Containers and you can see it :<\/p>\n <\/a><\/p>\n Now, before process the video we need to publish the asset<\/a>. Take care on the Content-length header’s value, it needs to be set at 0. I make a little update on the code.<\/p>\n Then, you will receive a 204 header code.<\/p>\n Ok, now, we have the asset and the file. But, in which format do we want to transcode ? Microsoft give us a list of presets<\/a> to make Task on WAMS : transcode in differents commons formats (for example iOS), apply protection (via PlayReady DRM) or rewrap contents from Smooth to HLS. I choose this preset :\u00a0H.264 iPod Classic \/ Nano.\u00a0<\/em>We can apply multiple tasks on an asset, but we will this it in a next post.<\/p>\n Let’s go to create our first job and apply a task preset.<\/p>\n This class provides methods to list, create and cancel a job. Here is a API creation call :<\/p>\n $assetUri<\/em> is given when you create the asset, $strConf<\/em> is the preset we choose (the string\u00a0H.264 iPod Classic \/ Nano<\/em>), $strTaskBody<\/em> is a XML string :<\/p>\n There is a last parameter : $strProcess<\/em>. This is the ID of the MediaProcessor we want to use. There is 4 types of MediaProcessors<\/a> :<\/p>\n In our case, we are using the Windows Azure Media Encoder, so apply the associated ID give by the MediaProcessor API on the $strProcess<\/em> param.<\/p>\n\n
Create an asset<\/h2>\n
class Rest{\r\n \tprotected $strUrl;\r\n \tprotected $strToken;\r\n \tprotected $strBody;\r\n \tpublic function __construct(){\r\n \t \t$this->requestToken();\r\n\t}\r\n\r\n\tprivate function generateData($arrData){\r\n\t\treturn implode('&', $arrData);\r\n\t}\r\n\r\n\tpublic function requestToken(){\r\n\t\tif (file_exists(TOKEN_STORAGE)){\r\n\t\t\techo 'Get Token from storage'."\\n";\r\n\t\t\t$data = file_get_contents(TOKEN_STORAGE);\r\n\t\t\t$arrToken = json_decode($data);\r\n\t\t\tif ((filemtime(TOKEN_STORAGE) + $arrToken->expires_in) > time()) {\r\n\t\t\t\t$this->strToken = $arrToken->access_token;\r\n\t\t\t} else {\r\n\t\t\t\techo 'Token expired'."\\n";\r\n\t\t\t\tunlink(TOKEN_STORAGE);\r\n\t\t\t\t$this->requestToken();\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\techo 'Get new token from API'."\\n";\r\n\t\t\t$arrData = array(\r\n\t\t\t\t'grant_type=client_credentials',\r\n\t\t\t\t'client_id='.CLIENT_ID,\r\n\t\t\t\t'client_secret='.urlencode(ACCESS_KEY),\r\n\t\t\t\t'scope=urn%3aWindowsAzureMediaServices'\r\n\t\t\t);\r\n\t\t\t$arrHeader = array(\r\n\t\t\t\t'Content-length:'.strlen($this->generateData($arrData))\r\n\t\t\t);\r\n\r\n\t\t\t$ch = curl_init();\r\n\t\t\tcurl_setopt($ch, CURLOPT_URL, TOKEN_URL);\r\n\t\t\tcurl_setopt($ch, CURLOPT_POSTFIELDS, $this->generateData($arrData));\r\n\t\t\tcurl_setopt($ch, CURLOPT_HTTPHEADER, $arrHeader);\r\n\t\t\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\r\n\t\t\t$data = curl_exec($ch);\r\n\t\t\tcurl_close($ch);\r\n\r\n\t\t\t$arrToken = json_decode($data);\r\n\t\t\tif (isset($arrToken->error)){\r\n\t\t\t\tprint_r($arrToken);\r\n\t\t\t\tdie();\r\n\t\t\t}\r\n\t\t\t$this->strToken = $arrToken->access_token;\r\n\t\t\tfile_put_contents(TOKEN_STORAGE, $data);\r\n\t\t\techo 'Token save in storage'."\\n";\r\n\t\t}\r\n\t\treturn $this->strToken;\r\n\t}\r\n\r\n\tpublic function request($arrData = array()){\r\n\t\t$ch = curl_init();\r\n\t\t$arrHeader = array(\r\n\t\t\t'x-ms-version:1.0',\r\n\t\t\t'DataServiceVersion:3.0',\r\n\t\t\t'MaxDataServiceVersion:3.0',\r\n\t\t\t'Authorization: Bearer '.$this->strToken,\r\n\t\t\t'Content-Type: application\/json;odata=verbose',\r\n\t\t\t'Accept: application\/json;odata=verbose'\r\n\t\t);\r\n\t\techo 'Call API:'.$this->strUrl."\\n";\r\n\t\tcurl_setopt($ch, CURLOPT_URL, $this->strUrl);\r\n\t\tcurl_setopt($ch, CURLOPT_HTTPHEADER, $arrHeader);\r\n\t\tcurl_setopt($ch, CURLOPT_HEADER, true);\r\n\t\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\r\n\t\tif (!empty($arrData)){\r\n\t\t\tcurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($arrData));\r\n\t\t}\r\n\t\t$data = curl_exec($ch);\r\n\t\t$arrInfo = curl_getinfo($ch);\r\n\t\tcurl_close($ch);\r\n\r\n\t\tlist($strHeader, $strBody) = explode("\\r\\n\\r\\n", $data, 2);\r\n\r\n\t\tif ($arrInfo['http_code'] == 301){\r\n\t\t\tif(preg_match('`\\s*Location:\\s(.+)`i', $data, $arrTmp)){\r\n\t\t\t\t$arrTmp[1] = trim($arrTmp[1]);\r\n\t\t\t\tRegister::getInstance()->setIndex('base_url', $arrTmp[1]);\r\n\t\t\t\t$this->strUrl = $arrTmp[1].$this->getVerb();\r\n\t\t\t\techo 'Redirection to '.$this->strUrl."\\n";\r\n\t\t\t\t$this->request($arrData);\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\t$this->strBody = $strBody;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate function getVerb(){\r\n\t\t$arrUrl = explode('\/', $this->strUrl);\r\n\t\treturn array_pop($arrUrl);\r\n\t}\r\n}<\/pre>\n
\n
class Asset extends Rest{\r\n\r\n\tpublic function __construct(){\r\n\t\tparent::__construct();\r\n\t}\r\n\r\n\tpublic function createAsset($strName) {\r\n\t\t$strAPIname = 'Assets';\r\n\t\t$this->strUrl = 'https:\/\/wamsbluclus001rest-hs.cloudapp.net\/API\/'.$strAPIname;\r\n\t\t$arrCreate = array('Name' => $strName);\r\n\t\t$this->request($arrCreate);\r\n\t}\r\n}\r\n\r\n$obj = new Asset();\r\n$str = $obj->createAsset('first_sintel_test');\r\n\r\nprint_r(json_decode($str));\r\n<\/pre>\n
public function listAsset() {\r\n\t\t$strAPIname = 'Assets';\r\n\t\t$this->strUrl = Register::getInstance()->getIndex('base_url').$strAPIname;\r\n\t\t$this->request();\r\n\t\treturn $this->strBody;\r\n\t}\r\n<\/pre>\n
\r\n$obj = new Asset();\r\n$str = $obj->listAsset();\r\n\r\nprint_r(json_decode($str));\r\n<\/pre>\n
Upload a file and associate to asset<\/h2>\n
\r\n$obj = new AccessPolicy();\r\n$str = $obj->createAssetPolicy('sintel_policy', 30, 2);\r\nprint_r(json_decode($str));\r\n<\/pre>\n
\n
\r\npublic function request($arrData = array(), $strTypePost = false){\r\n....\r\n\t\techo 'Call API:'.$this->strUrl."\\n";\r\n\t\tif ($strTypePost && empty($arrData)) {\r\n\t\t\tcurl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');\r\n\t\t\t$arrHeader[] = 'Content-Length: 0';\r\n\t\t}\r\n\t\tcurl_setopt($ch, CURLOPT_URL, $this->strUrl);\r\n....\r\n\t\t$this->strUrl = $arrTmp[1].$this->getVerb();\r\n\t\techo 'Redirection to '.$this->strUrl."\\n";\r\n\t\t$this->request($arrData, $strTypePost);\r\n....\r\n}<\/pre>\n
Create a job<\/h2>\n
class Jobs extends Rest{\r\n\r\n\tpublic function __construct(){\r\n\t\t$this->requestToken();\r\n\t}\r\n\r\n\tpublic function listJobs(){\uf72b\r\n\t\t$strAPIname = 'Jobs';\r\n\t\t$this->strUrl = Register::getInstance()->getIndex('base_url').$strAPIname;\r\n\t\t$this->request();\r\n\t\treturn $this->strBody;\r\n\t}\r\n\r\n\tpublic function createJob($strName, $strAssetUri, $strConfig, $strProcessor, $strTaskBody) {\r\n\t\t$strAPIname = 'Jobs';\r\n\t\t$this->strUrl = Register::getInstance()->getIndex('base_url').$strAPIname;\r\n\r\n\t\t$objMedia = new stdClass();\r\n\t\t$objMedia->__metadata->uri = $strAssetUri;\r\n\r\n\t\t$objTask = new stdClass();\r\n\t\t$objTask->Configuration = $strConfig;\r\n\t\t$objTask->MediaProcessorId = $strProcessor;\r\n\t\t$objTask->TaskBody = $strTaskBody;\r\n\r\n\t\t$arrCreate = new stdClass();\r\n\t\t$arrCreate->Name = $strName;\r\n\t\t$arrCreate->InputMediaAssets[] = $objMedia;\r\n\t\t$arrCreate->Tasks[] = $objTask;\r\n\r\n\t\t$this->request($arrCreate);\r\n\t\treturn $this->strBody;\r\n\t}\r\n\r\n\tpublic function cancelJob($assetId) {\r\n\t\t$strAPIname = 'CancelJob?jobid=';\r\n\r\n\t\t$this->strUrl = Register::getInstance()->getIndex('base_url').$strAPIname."'".urlencode($assetId)."'";\r\n\r\n\t\t$this->request();\r\n\t\treturn $this->strBody;\r\n\t}\r\n}<\/pre>\n
\r\n$objJob = new Jobs();\r\n$str = $objJob->createJob('EncoreSecondSintel', $assetUri, $strConf, $strProcess, $strTaskBody);\r\nprint_r(json_decode($str));\r\n<\/pre>\n
<?xml version="1.0" encoding="utf-16"?>\r\n<taskBody>\r\n\t<inputAsset>JobInputAsset(0)<\/inputAsset>\r\n\t<outputAsset>JobOutputAsset(0)<\/outputAsset>\r\n<\/taskBody><\/pre>\n
\n