Windows Azure MEDIA SERVICES. In action !!!

MediaServicesArch

De la misma manera que el el post anterior exprimíamos Windows Azure MOBILE SERVICES, en este caso le toca el turno a Windows Azure MEDIA SERVICES.

Pues bien, estos recientes servicios (PaaS), aun en Preview, tienen como finalidad, facilitar, crear y entregar contenido multimedia a los diferentes dispositivos: HTML5, Silverlight, Flash, Windows (Windows 8, etc.), Windows Phone, Xbox, MacOS, iPhone/iPad y Android, aportando gracias a Windows Azure, flexibilidad, escalabilidad y fiabilidad en la nube y, todo ello basado en “Microsoft Media Platform”.

  • Me gustaría hacer referencia a este enlace, donde Scott Guthrie, comenta brevemente el uso de estos servicios en las olimpiadas de Londres 2012, donde se estos han soportado unas 2.300h de vídeos en directo y contenido VOD (Video On demand)HD.

Y como siempre, Microsoft pone a nuestra disposición unos mágnificos tutoriales, donde poder seguir nuestros primeros pasos. Nosotros iremos un poco más allá hasta completar un ejemplo completo y funcional además de aclarar dudas y seguir algunas recomendaciones.

 

MEDIA SERVICES soporta los siguientes escenarios:

  • Building end-to-end workflows: Todo la aplicación en el Cloud.
  • Building hybrid workflows:  Integración de aplicaciones y procesos.  Pudiendo codificar por ejemplo, contenido en “On-Premisse” subiendo el mismo a Media Services para su transcodificación y transformación en distintos formatos.
  • Providing cloud support for media players: Crear, gestionar y distribuir/entregar contenido media a diferentes dispositivos y plataformas: iOS, Android, and Windows Devices.

Una vez ya conocemos las aspectos generales, veamos a continuación lo que necesitamos para llevar a cabo nuestro ejemplo: “MyFirstMedia Services”

1. Activamos el servicio para usarlo con nuestra subscripción.

2. Creamos un servicio desde la consola de administración (Portal):

imageimage

3. Aseguramos la sincronización de “key” con nuestro storage:

image

4. Creamos, desde Visual Studio un proyecto de tipo Consola

5. Añadimos las siguientes referencias.

  • C:Program FilesMicrosoft SDKsWindows Azure.NET SDK2012-06binMicrosoft.WindowsAzure.StorageClient.dll
  • C:Program Files (x86)Microsoft WCF Data Services5.0bin.NETFramework
    • Microsoft.Data.Edm.dll
    • Microsoft.Data.OData.dll
    • Microsoft.Data.Services.dll
    • Microsoft.Data.Services.Client.dll
    • System.Spatial.dll
  • C:Program Files (x86)Microsoft SDKsWindows Azure Media ServicesServicesv1.0Microsoft.WindowsAzure.MediaServices.Client.dll

6.  Conectamos con Media Services obteniendo un Contexto.  En este punto podemos obtener el siguiente error: ”Error 400 Bad Request when using CloudMediaContext with Azure Media Services” si no se introducen correctamente estos parámetros:

   1: this._context = new CloudMediaContext(accountName, accountKey);

Donde:

  • accountName es el nombre del servicio, introducido al crear el mismo y según hemos señalado como “Account Name”.
  • accountKey es el “SECONDARY MEDIA SERVICE ACCESS KEY”.

7. Creamos y encriptamos nuestro activo y lo subimos al Storage de Windows Azure, obteniendo una referencia al container de dichos ficheros. Si el valor de encriptación es “None”, podremos ver el contenido de dichos ficheros una vez realizado el “Upload” utilizando por ejemplo, “Azure Storage Explorer”.

   1: private IAsset UpLoadFiles(string[] files)

   2: {

   3:     // (2) Creamos un asset y añadimos los activos/ficheros para hacer el Upload del asset.            

   4:     return this._context.Assets.Create(files, AssetCreationOptions.StorageEncrypted);

   5: }

En este punto podremos obtener el error: “Could not load file or assembly ‘Microsoft.WindowsAzure.StorageClient, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)” que podemos resolver (gracias al cable de John) incluyendo en nuestro “.config” el siguiente código:

   1: <?xml version="1.0" encoding="utf-8" ?>

   2: <configuration>

   3:   <runtime>

   4:      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

   5:        <dependentAssembly>

   6:          <assemblyIdentity name="Microsoft.WindowsAzure.StorageClient"

   7:                            publicKeyToken="31bf3856ad364e35"

   8:                            culture="neutral" />

   9:          <bindingRedirect oldVersion="1.1.0.0"

  10:                           newVersion="1.7.0.0"/>

  11:        </dependentAssembly>

  12:        <dependentAssembly>

  13:          <assemblyIdentity name="Microsoft.WindowsAzure.StorageClient"

  14:                            publicKeyToken="31bf3856ad364e35"

  15:                            culture="neutral" />

  16:          <publisherPolicy apply="no" />

  17:        </dependentAssembly>

  18:  

  19:      </assemblyBinding>

  20:    </runtime>  

  21: </configuration>

Una vez todo ya podemos hacer el Upload de nuestro asset, ahora ya estos listo para lanzar el Job con el Encoding adecuado:

   1: public IJob CreateEncodingJob(IAsset asset, EnumMediaProcessor enumProcesor)

   2: {

   3:     // Declarar un nuevo Job.

   4:     IJob job = this._context.Jobs.Create("My encoding job");          

   5:  

   6:     string configuration = GetConfiguration(enumProcesor);

   7:     IMediaProcessor processor = this.GetMediaProcessor(enumProcesor);

   8:  

   9:  

  10:     // Crea un tarea con el detalle de codificacion(enconding) utilizando un string preestablecido.

  11:     // Strings preestablecidos para Windows Azure Media Encoder: http://msdn.microsoft.com/en-us/library/jj129582.aspx

  12:     ITask task = job.Tasks.AddNew("My encoding task",

  13:         processor,

  14:         configuration,

  15:         TaskCreationOptions.None);

  16:  

  17:     // Especificar el Asset a codificar

  18:     task.InputMediaAssets.Add(asset);

  19:  

  20:     

  21:     // Añade un asset de salida que contiene el resultado del job.

  22:     // Este asset no es encriptado "AssetCreationOption.None"

  23:     task.OutputMediaAssets.AddNew("Output asset",

  24:         true,

  25:         AssetCreationOptions.None);

  26:  

  27:     // Ejecutar el Job. 

  28:     job.Submit();

  29:  

  30:     return job;

  31: }

En este punto, tenemos que tener claras varias cosas:

  • El Tipo de “Media Processor” a utilizar dependerá  de lo que queramos hacer. Se trata de un string como puede verse en el siguiente snippet y, para conocer todos estos tipos preestablecidos accederemos a este enlace: “Task Preset Strings for Windows Azure Media Encoder”.
  • Cada uno de estos “Media Processor” trata un tipo de ficheros/asset determinado y de la misma manera genera una salida diferente. Por tanto si estamos lanzado un Job para:
    • una conversión de .MP4 a Smooth Streaming tendremos utilizar como “Media Processor”, “MP4 to Smooth Streams Task”, que genera un asset de salida: .ism, .isma, .ismc, .ismv, necesarios para los visores de smooth streaming.
    • una conversión   “Windows Azure Media Encoder” se genera una salida: .mp4, y “_metadata.xml”.
    • Etc. En este enlace, podemos ver todos los tipos de archivos soportados por Media Services.
  • Cada Job, además de trabajar con un “Media Processor” espera una configuración para el mismo, de manera que por ejemplo, para:
   1: // http://www.windowsazure.com/en-us/develop/net/how-to-guides/media-services/

   2: // Windows Azure Media Encoder:  Media Encoder. 

   3: // PlayReady Protection Task:   PlayReady Protection. 

   4: // MP4 to Smooth Streams Task:  Conversión de  .mp4 a formato smooth streaming. 

   5: // Smooth Streams to HLS Task:  Conversión de smooth streaming a formato Apple HTTP Live Streaming (HLS). 

   6: // Storage Decryption:          Desencripta los assets que has sindo encriptados usando Storage Encription. 

   7: private IMediaProcessor GetMediaProcessor(EnumMediaProcessor mediaProcessor)

   8: {

   9:     var theProcessor =

  10:         from p in this._context.MediaProcessors

  11:         where p.Name == mediaProcessor.GetStringValue()

  12:         select p;

  13:  

  14:     IMediaProcessor processor = theProcessor.First();

  15:  

  16:     if (processor == null)

  17:     {

  18:         throw new ArgumentException(string.Format(System.Globalization.CultureInfo.CurrentCulture,

  19:             "Unknown processor",

  20:             mediaProcessor));

  21:     }

  22:     return processor;

  23: }

Si el Job ha terminado correctamente nuestra aplicación cliente podría acceder al asset y descargarlo. No obstante se puede producir erres durante la la codificación, donde para ello, haremos un chequeo del Job para corroborar su estado:

   1: private static ReadOnlyCollection<ErrorDetail> CheckJobProgress(string jobId)

   2: {

   3:     bool jobCompleted = false;

   4:     ReadOnlyCollection<ErrorDetail> jobErrors = null;

   5:  

   6:     while (!jobCompleted && (null == jobErrors))

   7:     {

   8:         IJob theJob = _manager.GetJob(jobId);

   9:         switch (theJob.State)

  10:         {

  11:             case JobState.Finished:

  12:                 jobCompleted = true;

  13:                 break;

  14:             case JobState.Queued:

  15:             case JobState.Scheduled:

  16:             case JobState.Processing:

  17:                 Thread.Sleep(5000);

  18:                 Console.Write(".");

  19:                 break;

  20:             case JobState.Error:

  21:                 jobErrors = theJob.Tasks[0].ErrorDetails;

  22:                 break;

  23:             default:

  24:                 Console.WriteLine("Unknown job state: {0}", theJob.State.ToString());

  25:                 break;

  26:         }

  27:     }

  28:     return jobErrors;

  29: }

A pesar de tener en cuenta las recomendaciones anteriores, se pueden producir algunos errores durante el “Upload” o el “Endonding del Job”

  • “Cannot import a zero-lenght file”: Se trata de error al subir un fichero de tipo imagen e intentar realizar su enconding distinto a “Storage Decryption”.
  • “One or more errors occurred” : Generalmente este se produce si intentamos subir ficheros a través de una conexión débil como por ejemplo desde una 3G con poca cobertura. El detalle de estos puede verse a continuación:

InnerException: System.Data.Services.Client.DataServiceClientException

            HResult=-2146233079

            Message=<?xml version="1.0" encoding="utf-8" standalone="yes"?><error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"><code></code><message xml:lang="en-US">Asset has no files uploaded.</message></error>

            Source=Microsoft.Data.Services.Client

            StatusCode=400

            StackTrace:

                 at System.Data.Services.Client.QueryResult.ExecuteQuery(DataServiceContext context)

                 at System.Data.Services.Client.DataServiceRequest.Execute[TElement](DataServiceContext context, QueryComponents queryComponents)

Donde Fiddler nos proporcional la siguiente información:

  • 500: <?xml version="1.0" encoding="utf-8"?><Error><Code>OperationTimedOut</Code><Message>Operation could not be completed within the specified time.

    RequestId:fc8be06f-1801-4724-a549-14a5d327d48f

    Time:2012-09-23T13:33:32.3638228Z</Message></Error>

  • 504:ReadResponse() failed: The server did not return a response for this request.
  • 502: The socket connection to mediasvc5s9w447krcczl.blob.core.windows.net failed. <br />ErrorCode: 10060. <br />A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 168.63.0.14:80
  • DNS Lookup for "mediasvc5s9w447krcczl.blob.core.windows.net" failed. No such host is known.    

Otros recomendaciones que deberíamos conocer son:

  • En la preview actual, no es posible eliminar los “Assets” subidos de ninguna manera desde la consola.  Mediante código sería de la siguiente manera, teniendo en cuenta que antes de poder borrar un Asset, tenemos que revocar (“Revoke”) su Locator. No obstante y a pesar de todo, en mis pruebas no lo he conseguido.  Supongo que es debido a la versión “Preview”:
   1: foreach (var a in this._context.Assets)

   2: {

   3:     foreach (var l in a.Locators)

   4:         this._context.Locators.Revoke(l);               

   5:  

   6:         this._context.Assets.Delete(a); 

   7: }

  • Incluyo a continuación un par de snippets que nos facilitarán los progresos de carga y descarga para así no desesperarnos:

Progreso de carga (“Upload”) de ficheros/assets:

   1: ...

   2:        _manager.Assets.OnUploadProgress += Assets_OnUploadProgress;

   3: ..

   4: rivate static void Assets_OnUploadProgress(object sender, UploadProgressEventArgs e)

   5:  

   6:    try

   7:    {

   8:        Console.WriteLine("Assets_OnUploadProgress: {0:0.00} %, {1}/{2}, {3:0.00} MB/{4:0.00} MB",

   9:        e.Progress, e.CurrentFile, e.TotalFiles, e.BytesSent / 1024 ^ 2, e.TotalBytes / 1024 ^ 2);

  10:    }

  11:    catch (Exception ex)

  12:    {

  13:        Console.WriteLine("OnUploadProgress event error: {0}{1}{2}", ex.Message, Environment.NewLine, ex.StackTrace.ToString());

  14:    }

Progreso de descarga (“Download”):

   1: private static void DownloadAssetToLocal(IAsset outputAsset, string outputFolder)

   2: {

   3:     Console.WriteLine();

   4:     Console.WriteLine("Files are downloading...please wait.");

   5:     Console.WriteLine();

   6:  

   7:     foreach (IFileInfo outputFile in outputAsset.Files)

   8:     {

   9:         outputFile.OnDownloadProgress += outputFile_OnDownloadProgress;

  10:  

  11:         string localDownloadPath = Path.GetFullPath(outputFolder + @"" + outputFile.Name);

  12:         Console.WriteLine("File is downloading to:  " + localDownloadPath);

  13:         outputFile.DownloadToFile(Path.GetFullPath(outputFolder + @"" + outputFile.Name));

  14:         Console.WriteLine();

  15:     }

  16: }

  17:  

  18: static void outputFile_OnDownloadProgress(object sender, DownloadProgressEventArgs e)

  19: {

  20:     Console.WriteLine("    Bytes downloaded: " + e.BytesDownloaded);

  21:     Console.WriteLine("    Download progress %:  " + e.Progress);

  22: }

Espero haber conseguido con este recorrido a través de Media Services acercarnos un poco más al mundo Cloud con Windows Azure.


Saludos @Home

Juanlu, ElGuerre

Windows Azure MOBILE SERVICES & Windows 8

mobile-service

Muy buenas,

Hoy, me disponía a ver el funcionamiento de la reciente nueva característica de Windows Azure (“MOBILE SERVICES”) y sinceramente, ha sido bastante fácil e intuitivo entender todo lo que a ello respecta. ¡La verdad es que los tutoriales que ha preparado Microsoft son geniales además de sencillos!. Podemos encontralos aquí, en “Get Started With Mobile Services”. 

Scott Guthrie también ha escrito un post al respecto, así, que la intención de este post será profundizar un poco más e indagar en algunos otros detalles.

Veamos a continuación como funciona todo esto:

1. Habilitar esta característica. Para ello seguiremos estos pasos.

2. Accedemos al nuevo portal de Windows Azure.

3. Seguimos los pasos del tutorial de Microsoft: “Get Started With Mobile Services” donde:

  • El primer paso es Instalar el SDK para Mobile Services además de tener instalado Visual Studio
  • El segundo es Crear una aplicación nueva o conectarse a una ya existente.

4. Finalizado el tutorial y, sin esfuerzo alguno, tendremos una aplicación Windows 8 conectada a Windows Azure.

Nota: Pero, ¿Cómo funciona todo por “debajo”? Pues bien, veamos los dos aspectos más importantes:

a. Conexión con  el servicio. En la clase “App.xaml.cs”, encontraremos como crear dicha conexión con Mobile Services:

   1: public static MobileServiceClient MobileService = new MobileServiceClient(

   2:     "https://myfirstmobileservice.azure-mobile.net/",

   3:     "rbLHUGEnPzdeaXvftueqBnJyMXRuiD76"

   4: );

Donde:

  • La línea 2 es la dirección (URL) bajo la que se encuentra alojado el servicio en Windows Azure y que coincide, con el nombre que hemos introducido al crear nuestro nuevo Mobile Services “myfirstmobileservice”. El sufijo, “.azure-mobile.net” será el mismo para todos nuestros mobile services.
  • La linea 3, es nuestra “Application Key”, es decir, la clave que necesitamos para poder establecer la conexión con el servicio.  La podemos obtener o cambiar siguiendo estos dos simples pasos:

imageimage

Nota: En caso de que esta key no se corresponda la que hemos indicado en el código , obtendremos un error: “Unauthorized  (401 Unauthorized – Details: {“code”:401,”error”:”Unauthorized”})”.

b. Conexión con la BBDD: En el ejemplo y concretamente en el paso 2 del tutorial, podemos crear una tabla de ejemplo (“Create TodoItem Table”). Sin embargo, para profundizar más, veamos como cambiar la misma y añadir un par de campos nuevos: “StartDate” y “DueDate” para así poner en calendario nuestras tareas.

Lo primero es modificar la tabla “TodoItem”. Pero para esta acción, necesitaremos acceder a la misma, (a SQL Azure), a través del portal de SQL Azure, o bien, a través de la conexión desde Management Studio, o mejor aún, a través del Server Explorer de Visual Studio 2012, que ahora permite la edición de las tablas de SQL Azure en modo diseño:

image image

En mi caso, y para este ejemplo, opté por crear la tabla “TodoItem”  en mi BBDD “Northwind” que ya tengo creada en SQL Azure.

A continuación, lo siguiente es cambiar nuestro “Model”  C#, para incluir estas dos nuevas propiedades. Para el ejemplo en cuestión,  nuestro modelo se encuentra en la clase “MainPage.xaml.cs”:

   1: public class TodoItem

   2: {

   3:     public int Id { get; set; }

   4:  

   5:     [DataMember(Name = "text")]

   6:     public string Text { get; set; }

   7:  

   8:     [DataMember(Name = "complete")]

   9:     public bool Complete { get; set; }

  10:  

  11:     [DataMember(Name ="StartDate")]

  12:     public DateTime StartDate {get; set;}

  13:         

  14:     [DataMember(Name = "DueDate")]

  15:     public DateTime DueDate {get; set;}

  16: }

 

Nota: Recordemos que los nombres de los campos de la tabla deben coincidir con estas propiedades o con los indicados en la propiedad Name de “DataMember”. Recordad tambien que estos valores son “key sensitive”.

Por ultimo, sólo tenemos que añadir la introducción de estos dos nuevos valores en el código, al hacer el guardado. Podríamos haber incluido dos “DatePicker” para ambas fechas, pero para el ejemplo, bastará con verlo en el código:

   1: private void ButtonSave_Click(object sender, RoutedEventArgs e)

   2: {

   3:     var todoItem = new TodoItem { Text = TextInput.Text, StartDate = DateTime.Now,  DueDate = DateTime.Now.AddDays(1) };

   4:     InsertTodoItem(todoItem);

   5: }

Como vemos, simplemente se trata de pasar estos dos nuevos parámetros al insertar la nueva tarea. Ahora incluimos en la vista dos nuevos TextBlock en el “DataTemplate” del “ListView” y listo:

   1: <ListView Name="ListItems" Margin="62,10,0,0" Grid.Row="1">

   2:     <ListView.ItemTemplate>

   3:         <DataTemplate>

   4:             <StackPanel Orientation="Horizontal">

   5:                 <CheckBox Name="CheckBoxComplete" IsChecked="{Binding Complete, Mode=TwoWay}" Checked="CheckBoxComplete_Checked" Content="{Binding Text}" Margin="10,5" VerticalAlignment="Center"/>

   6:                 <TextBlock Name="StartDate" Text="{Binding StartDate}" Margin="10,5" />                                                                    

   7:                 <TextBlock Name="DueDate" Text="{Binding DueDate}" Margin="10,5" />

   8:             </StackPanel>

   9:         </DataTemplate>

  10:     </ListView.ItemTemplate>

  11: </ListView>

Ejecutamos y ya tenemos nuestra aplicación Windows 8 conectada a Azure y con nuestros cambios realizados en la base de datos (BBDD) SQL Azure.

image

c. Adicionalmente existen otros dos puntos a tener en cuenta y que también son incluidos en los tutoriales:

Authenticación: Para lo que tendremos que descargarnos el SDK de Live para Windows.

Notificaciones Push: Permitirá a nuestra aplicación Windows 8 recibir notificaciones. En el caso del ejemplo, tras cada inserción. Para llevar a cabo esta tarea necesitaremos la actualización de los scripts (JavaScript) de nuestros servicios. Esto podremos hacerlo accediendo a la pestaña “Data” y, a continuación, y tras seleccionar una tabla, “TodoItem” para nuestro ejemplo, seleccionando la pestaña “Script”.  Para más detalle de estos scripts podemos echar un vistazo a: Mobile Services scripting.  Adicionalmente y gracias a “Mobile Services Scripting”, podremos realizar validaciones en servidor.

Espero haber aclarado un poco más lo fácil que es disponer de una aplicación Windows 8 conectada con Windows Azure gracias a “MOBILE SERVICES”.  Y, ahora que todos estamos tan contentos haciendo nuestros pinitos con Windows 8, no hay impedimentos para no tener nuestros datos en la nube, Guiño.

Este es el primer paso desde “Mobile Services” para hacernos tan fácil nuestro trabajo, pero, hay más por venir: Integración con Windows Azure Storage, Servicios Rest, etc. En este punto surgen algunas dudas como, ¿Tendremos integración de Mobile Services para iPone/iPad, Android, etc? ¡Habrá que esperar!

Saludos @Home
Juanlu, ElGuerre (@JuanluElGuerre)