Obteniendo los datos
Las notificaciones de Dynamic Ingest le brindan toda la información que necesita para saber cuando su video está listo; solo necesita saber qué buscar ... y definir qué significa "listo" para sus sistemas. Este diagrama resume el flujo de trabajo:
Notificaciones de ingesta dinámica
El servicio de notificación Dynamic Ingest le envía notificaciones para varios tipos de eventos. Los tres que son más útiles para determinar cuándo el video está "listo" son los que indican que se han creado versiones particulares, los que indican que se ha creado un manifiesto y el que indica que todo el procesamiento está completo. Aquí hay ejemplos de cada uno:
Notificación de reproducción creada
Tenga en cuenta en este ejemplo:
- La
videoId
El valor le permite saber para qué video es la reproducción (en caso de que tenga varios trabajos de ingesta en ejecución) - La
profileRefId
valor es el ID de referencia para la interpretación especificada en el perfil de ingesta - Si el
status
El valor es "SUCCESS", la interpretación se creó correctamente. - Para un tipo segmentado como HLS o MPEG-DASH, la existencia de la interpretación no hace que se pueda reproducir; también necesita el manifiesto apropiado (consulte el siguiente ejemplo). Las versiones MP4 se pueden reproducir tan pronto como se crean.
Notificación de manifiesto creado
Tenga en cuenta en este ejemplo:
- La
videoId
El valor le permite saber para qué video es la reproducción (en caso de que tenga varios trabajos de ingesta en ejecución) - La
profileRefId
value es un código especial que le dice que el activo creado era un manifiesto HLS (los otros valores posibles sonHdsManifest
,DashManifest
, ySmoothIsmManifest
) - Para HLS y HDS, se creará un manifiesto, por lo que verá una notificación. Para DASH y SmoothIsm, se crean dos manifiestos (uno para usar en la API de medios heredada, el otro para la CMS API), por lo que verá dos notificaciones de este tipo.
- Si el
status
el valor es "SUCCESS", el manifiesto se creó correctamente - Para un tipo segmentado como HLS o MPEG-DASH, no existe un orden definido para la creación de las representaciones y el manifiesto; estas representaciones no se pueden reproducir hasta que se crean ambas (o el video se ha procesado por completo; consulte el siguiente ejemplo).
Procesando notificación completa
Tenga en cuenta en este ejemplo:
- La
videoId
El valor le permite saber para qué video es la reproducción (en caso de que tenga varios trabajos de ingesta en ejecución) - La
profileRefId
es no incluido en esta notificación - Si el
status
El valor es "SUCCESS", el video se procesó correctamente.
Para recibir notificaciones, debe incluir un campo "devoluciones de llamada" en usted API de ingesta dinámica solicitudes, apuntando a una o más direcciones de devolución de llamada:
{
"master": {
"url": "https://s3.amazonaws.com/bucket/mysourcevideo.mp4"
}, "profile": "high-resolution",
"callbacks": ["http://host1/path1”, “http://host2/path2”]
}
Panel de control de muestra
Esta sección explica cómo se pueden juntar las notificaciones para construir un tablero simple para la API de Dynamic Ingest. El controlador de notificaciones analiza las notificaciones del API de ingesta dinámica para identificar el procesamiento de notificaciones completas. Luego agrega las notificaciones de video en una matriz de objetos para cada video en un archivo JSON. El tablero en sí es una página HTML que importa el archivo JSON para obtener los datos de notificación. Utiliza los identificadores para realizar una solicitud al CMS API para obtener los metadatos del video. Puedes ver el tablero aquí.
Todos los archivos de esta aplicación, junto con las instrucciones para configurarla para su cuenta, están en este repositorio.
Aquí está la arquitectura de alto nivel de la aplicación:
Las partes de la aplicación
El controlador de notificaciones está integrado en PHP: busca procesar notificaciones completas y agrega la identificación del video a una matriz en un archivo JavaScript separado:
<? php // var para registrar errores, si los hay $ problem = "Sin errores"; // var para almacenar el índice de video actual $ videoIndex = -1; // obtener datos de entrada intentar { $json = file_get_contents('php://input'); $decoded = json_decode($json, true); } captura (Excepción $ e) { $problem = $e->getMessage(); echo $problem; } // obtener el contenido del archivo de datos y analizarlo intente { $notificationData = file_get_contents('di.json'); $notificationDataDecoded = json_decode($notificationData, true); } captura (Excepción $ e) { $problem = $e->getMessage(); echo $problem; } if (isset ($ decoded ["entityType"])) { $entityType = $decoded["entityType"]; // if the entity type is ASSET or TITLE, add it to notification data array if ($entityType == "ASSET" || $entityType == "TITLE") { array_push($notificationDataDecoded, $decoded); } // ahora reemplazaremos el contenido de di.json con lo que tenemos file_put_contents ('di.json', json_encode ($ notificationDataDecoded)); } echo "La aplicación de devolución de llamada Dynamic Ingest se está ejecutando"; var_dump ($ notificationData); ?>
Archivo JSON:
El archivo JSON es inicialmente una matriz vacía ([]
): el controlador de notificaciones agrega los datos.
Tablero
El tablero incluye HTML y JavaScript para obtener los datos de notificación y datos de video adicionales del CMS API y escribe los resultados en una tabla:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Dynamic Ingest Log</title>
<style>
body {
font-family: sans-serif;
margin: 5em;
}
.hide {
display: none;
}
.show {
display: block;
}
table {
border-collapse: collapse;
border: 1px #999999 solid;
}
th {
background-color: #666666;
color: #f5f5f5;
padding: .5em;
font-size: .7em;
}
td {
border: 1px #999999 solid;
font-size: .7em;
padding: .5em
}
.hidden {
display: none;
}
</style>
</head>
<body>
<h1>Dynamic Ingest Log</h1>
<h2>Account: Brightcove Learning (57838016001)</h2>
<p style="width:70%">
Videos are listed in order of processing completion time, newest to oldest. The reference id (generated by the <a href="./di-tester.html">Dynamic Ingest tester</a>) is a combination of the date/time that the Dynamic Ingest job was initiated and the ingest profile that was used. You can add additional videos using the <a href="./di-tester.html">Dynamic Ingest tester</a>. New videos will appear in this log after processing is complete.
</p>
<p>
<button id="clearLogBtn">Clear the log</button>
</p>
<div id="videoLogBlock">
<table>
<thead>
<tr>
<th>Video ID</th>
<th>Name</th>
<th>Reference ID</th>
<th>HLS Manifests Created</th>
<th>HLS Renditions Created</th>
<th>MP4 Renditions Created</th>
<th>Processing Complete</th>
</tr>
</thead>
<tbody id="logBody"></tbody>
</table>
<h4 id="loadingMessage">Loading data, please wait...</h4>
</div>
<script>
var BCLS = ( function (window, document) {
// to use another account, set the account_id value appropriately
// the client_id and client_secret will also need to be changed in the proxy
var my_account_id = 57838016001,
account_id = my_account_id,
logBody = document.getElementById('logBody'),
loadingMessage = document.getElementById('loadingMessage'),
clearLogBtn = document.getElementById('clearLogBtn'),
i = 0,
iMax,
// set the proxyURL to the location of the proxy app that makes Brightcove API requests
proxyURL = './brightcove-learning-proxy.php',
dataFileURL = './di.json',
videoDataArray = [],
requestOptions = {},
currentVideo,
currentIndex = 0;
/**
* Logging function - safe for IE
* @param {string} context - description of the data
* @param {*} message - the data to be logged by the console
* @return {}
*/
function bclslog(context, message) {
if (window["console"] && console["log"]) {
console.log(context, message);
}
return;
}
/**
* tests for all the ways a variable might be undefined or not have a value
* @param {*} x the variable to test
* @return {Boolean} true if variable is defined and has a value
*/
function isDefined(x) {
if ( x === '' || x === null || x === undefined || x === NaN) {
return false;
}
return true;
}
/**
* find index of an object in array of objects
* based on some property value
*
* @param {array} targetArray - array to search
* @param {string} objProperty - object property to search
* @param {string|number} value - value of the property to search for
* @return {integer} index of first instance if found, otherwise returns null
*/
function findObjectInArray(targetArray, objProperty, value) {
var i, totalItems = targetArray.length, objFound = false;
for (i = 0; i < totalItems; i++) {
if (targetArray[i][objProperty] === value) {
objFound = true;
return i;
}
}
if (objFound === false) {
return null;
}
}
/**
* factory for new video objects
* @param {String} videoId the video id
* @return {object} the new object
*/
function makeVideoDataObject(videoId) {
var obj = {};
obj.id = videoId;
obj.name = '';
obj.reference_id = '';
obj.hlsManifests = 0;
obj.hlsRenditions = 0;
obj.mp4Renditions = 0;
obj.complete = 'no';
return obj;
}
/**
* processes notification objects
* creates a new object in the videoDataArray if it doesn't exist
* and updates the videoDataArray object based on the notification
* @param {Object} notificationObj the raw notification object
*/
function processNotification(notificationObj) {
var objIndex, videoObj;
// if notification object contains a video id, find the corresponding
// object in the videoDataArray or create it if it's not there
if (isDefined(notificationObj) && isDefined(notificationObj.videoId)) {
objIndex = findObjectInArray(videoDataArray, 'id', notificationObj.videoId);
// if not found, create one
if (!isDefined(objIndex)) {
videoObj = makeVideoDataObject(notificationObj.videoId);
videoDataArray.push(videoObj);
objIndex = videoDataArray.length - 1;
}
// now update properties based on what's in the notification
if (notificationObj.entityType === 'ASSET') {
// if it's a rendition or manifest, there will be a profileRefId
if (isDefined(notificationObj.profileRefId)) {
// see if it's an HLS manifest
if (notificationObj.profileRefId === 'HlsManifest') {
// increment the hls manifest count
videoDataArray[objIndex].hlsManifests++;
} else if (notificationObj.profileRefId.charAt(0) === 't') {
// increment the hls rendition count
videoDataArray[objIndex].hlsRenditions++;
} else if (notificationObj.profileRefId.charAt(0) === 'm') {
// increment the mp4 rendition count
videoDataArray[objIndex].mp4Renditions++;
}
}
} else if (notificationObj.entityType === 'TITLE') {
// overall processing notification - checked for SUCCESS / FAILED
if (notificationObj.status === 'SUCCESS') {
// mark complete
videoDataArray[objIndex].complete = 'yes';
} else if (notificationObj.status === 'FAILED') {
// mark failed
videoDataArray[objIndex].complete = 'failed';
}
}
}
return;
}
/**
* creates the dashboard table body
*/
function writeReport() {
var j,
jMax = videoDataArray.length,
item,
t;
loadingMessage.textContent = 'This page will refresh in 1 minute...';
/* just showing HLS and MP4 renditions, because
* that's all that will be produced in this account,
* but you could modify the notification handler and
* this page to handle other formats
*/
for (j = 0; j < jMax; j++) {
item = videoDataArray[j];
if (item.id !== undefined) {
logBody.innerHTML += '<tr><td>' + item.id + '</td><td>' + item.name + '</td><td>' + item.reference_id + '</td><td>' + item.hlsManifests + '</td><td>' + item.hlsRenditions + '</td><td>' + item.mp4Renditions + '</td><td>' + item.complete + '</td></tr>';
}
}
// set timeout for refresh
t = window.setTimeout(init, 60000);
};
// function to set up the notification data request
function setJSONRequestOptions() {
submitRequest(null, dataFileURL, 'notificationData');
}
// function to set up video data request
function setVideoRequestOptions() {
requestOptions = {};
requestOptions.url = 'https://cms.api.brightcove.com/v1/accounts/' + account_id + '/videos/' + currentVideo.id;
submitRequest(requestOptions, proxyURL, 'video');
}
/**
* initiates the CMS API requests
*/
function getVideoInfo() {
iMax = videoDataArray.length;
if (currentIndex < iMax) {
currentVideo = videoDataArray[currentIndex];
setVideoRequestOptions();
} else {
loadingMessage.innerHTML = 'No videos have been ingested - you can add some using the <a href="./di-tester.html">Dynamic Ingest tester</a>';
}
}
/**
* make the CMS API requests
* @param {Object} options request options
* @param (String) url URL to send request to
* @param (String) type the request type
*/
function submitRequest(options, url, type) {
var httpRequest = new XMLHttpRequest(),
requestData,
responseData,
videoDataObject,
parsedData,
getResponse = function () {
try {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
responseData = httpRequest.responseText;
switch (type) {
case 'notificationData':
var k, kMax, dataArray;
dataArray = JSON.parse(responseData);
bclslog('dataArray', dataArray);
// process the notifications
kMax = dataArray.length;
for (k = 0; k < kMax; k++) {
processNotification(dataArray[k]);
}
getVideoInfo();
break;
case 'video':
parsedData = JSON.parse(responseData);
bclslog('parsedData', parsedData);
videoDataArray[currentIndex].reference_id = parsedData.reference_id;
videoDataArray[currentIndex].name = parsedData.name;
currentIndex++;
if (currentIndex < iMax) {
currentVideo = videoDataArray[currentIndex];
setVideoRequestOptions();
} else {
writeReport();
}
break;
}
} else {
bclslog("There was a problem with the request. Request returned " + httpRequest.status);
if (type === 'video') {
setVideoRequestOptions();
} else {
setSourcesRequestOptions();
}
}
}
}
catch(e) {
bclslog('Caught Exception: ' + e);
}
};
// notifications data is a special case
if (type === 'notificationData') {
// set response handler
httpRequest.onreadystatechange = getResponse;
// open the request
httpRequest.open("GET", url);
// set headers
httpRequest.setRequestHeader("Content-Type", "application/json");
// open and send request
httpRequest.send();
} else {
// requests via proxy
// set up request data
requestData = "url=" + encodeURIComponent(options.url) + "&requestType=GET";
// set response handler
httpRequest.onreadystatechange = getResponse;
// open the request
httpRequest.open("POST", url);
// set headers
httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// open and send request
httpRequest.send(requestData);
}
};
// event handlers
clearLogBtn.addEventListener('click', function () {
if (window.confirm('Are you sure? This action cannot be undone!')) {
// if your clear-log app resides in another location, change the URL
window.location.href = 'clear-log.php';
}
});
// get things started
function init() {
// clear table and the video data array
logBody.innerHTML = "";
videoDataArray = [];
setJSONRequestOptions();
}
// kick off the app
init();
})(window, document);
</script>
</body>
</html>
Apoderado
<? php / ** * brightcove-learning-proxy.php - proxy para las API RESTful de Brightcove * obtiene un token de acceso, realiza la solicitud y devuelve la respuesta * Accediendo a: * URL: https://solutions.brightcove.com/ bcls / bcls-proxy / bcsl-proxy.php * (tenga en cuenta que debe * siempre * acceder al proxy a través de HTTPS) * Método: POST * * @post {string} url: la URL de la solicitud de API * @post {string} [requestType = GET]: método HTTP para la solicitud * @post {string} [requestBody = null]: datos JSON que se enviarán con solicitudes de escritura * * @returns {string} $ respuesta: respuesta JSON recibida de la API * / // encabezado de habilitación de CORS ("Access-Control-Allow-Origin: *"); // configurar la solicitud para el token de acceso $ data = array (); // // cambia los valores a continuación para usar este proxy con una cuenta diferente // $ client_id = "YOUR_CLIENT_ID_HERE"; $ client_secret = "YOUR_CLIENT_SECRET_HERE"; $ auth_string = "{$client_id}:{$client_secret}"; $ request =" https://oauth.brightcove.com/v4/access_token?grant_type=client_credentials "; $ ch = curl_init ($ request); curl_setopt_array ($ ch, array (CURLOPT_POST => TRUE, CURLOPT_RETURNTRUEFER => TRUEANSFER , CURLOPT_SSL_VERIFYPEER => FALSE, CURLOPT_USERPWD => $ auth_string, CURLOPT_HTTPHEADER => array ('Tipo de contenido: aplicación / x-www-form-urlencoded',), CURLOPT_POSTFIELDS => $ respuesta) ); curl_close ($ ch); // Verifica si hay errores si ($ respuesta === FALSO) { die(curl_error($ch)); } // Decodifica la respuesta $ responseData = json_decode ($ response, TRUE); $ access_token = $ responseData ["access_token"]; // configurar la llamada a la API // obtener datos si ($ _POST ["requestBody"]) { $data = json_decode($_POST["requestBody"]); } demás { $data = array(); } // obtiene el tipo de solicitud o el valor predeterminado es GET if ($ _POST ["requestType"]) { $method = $_POST["requestType"]; } demás { $method = "GET"; } // obtener la URL y la información de autorización del formulario data $ request = $ _POST ["url"]; // envía la solicitud http $ ch = curl_init ($ request); curl_setopt_array ($ ch, array (CURLOPT_CUSTOMREQUEST => $ método, CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_SSL_VERIFYPEER => FALSE, CURLOPT_HTTPHEADER => array ('Content-type', application / Authorization Portador {$access_token}",), CURLOPT_POSTFIELDS => json_encode ($ data))); $ respuesta = curl_exec ($ ch); curl_close ($ ch); // Verifica si hay errores si ($ respuesta === FALSE) { echo "Error: "+$response; die(curl_error($ch)); } // Decodifica la respuesta // $ responseData = json_decode ($ response, TRUE); // devuelve la respuesta al llamador AJAX echo $ response; ?>
Limpiar el registro
Esta sencilla aplicación PHP simplemente restaura el archivo JavaScript a su estado original, borrando los viejos identificadores de video:
<? php $ logFileLocation = "di.json"; $ freshContent = array (); $ encodedContent = json_encode ($ freshContent); file_put_contents ($ logFileLocation, $ encodedContent); echo 'Archivo de registro borrado - <a href="di-log.html"> volver al panel </a>'; ?>