Quantcast
Channel: Planeta Código
Viewing all 2734 articles
Browse latest View live

Koalite: Programadores Políglotas y Proyectos Políglotas

$
0
0

Torre de Babel

Hace unos años sonaba bastante el término programador políglota para referirse a aquellos programadores que eran capaces de trabajar con varios lenguajes de programación. Hoy en día es una idea que ha perdido popularidad, al menos a la hora de rellenar posts y charlas en eventos. Es una idea que me gusta, y querría pensar que si ya no se habla tanto de ello es porque se ha convertido en algo que se da por hecho, pero en realidad creo que, simplemente, ha pasado a un segundo plano y las modas actuales van por otro lado.

En paralelo sí que veo un auge de lo que podríamos considerar proyectos políglotas, en los que se mezclan distintos lenguajes de programación, y no sólo como lenguajes auxiliares, sino como lenguajes primarios en diferentes áreas del proyecto. Aquí la moda de los microservicios tiene mucho que decir.

Por supuesto, esto no es más que una impresión personal y limitada a mi círculo de fuentes de información, así que es probable que esté equivocado. Aun así, aprovecharé para analizar ambas ideas: la del programador y la del proyecto políglota.

Las ventajas del programador políglota

En un mundo de fantasía uno aprendería a programar en un lenguaje y ya sabría programar en todos. Tan sólo es cuestión de aprender la sintaxis de cada uno y traducir de uno a otro.

¿Suena iluso? Sin duda, pero es una sensación que muchos hemos tenido. Durante mis primeros años en esto, era lo que pensaba. Cuando aprendí a programar en Pascal y luego en C, daba por hecho que era más o menos lo mismo, pero cambiando los BEGIN/END por {/}. Bueno, puede que entre Pascal y C no haya taaaaanta diferencia, pero es algo más que un cambio de sintaxis, sobre todo cuando piensas en los estilos de programación que imperan en cada uno.

Asumiendo entonces que aprender un lenguaje de programación es algo más que aprender su sintaxis, esto de aprender varios, y más si quieres tener un grado de soltura razonable con ellos, parece que va a requerir cierto esfuerzo. Además de la sintaxis, cada lenguaje viene con su paquete de herramientas, librerías, frameworks, estilos de programación y construcciones idiomáticas, y manejarse bien con todo ello son palabras mayores.

¿Merece la pena dedicar todo ese esfuerzo en conocer otros lenguajes?

Teniendo en cuenta que para muchos su trabajo en el día a día les lleva a utilizar un único lenguaje de programación (o un par de ellos), parece que lo más razonable es profundizar en ellos al máximo y ser lo más productivo posible con ellos. A fin de cuentas, es difícil que dedicando un rato a jugar con otros lenguajes alcances el grado de productividad que tienes con un lenguaje, unas herramientas y unas librerías a las que dedicas 30 o 40 horas semanales.

Sin embargo, y como ya te imaginarás puesto que estoy escribiendo este post, soy un firme defensor de aprender otros lenguajes de programación, aunque nunca llegues a dominarlos tanto como tu lenguaje “del día a día” y nunca llegues a utilizarlos “profesionalmente”.

Cuando trabajas siempre con las mismas herramientas y eres productivo con ellas es muy fácil acomodarse y asumir que es lo mejor que vas a encontrar. De hecho, probablemente sea cierto y, como decía antes, en este momento ninguna herramienta te haga tan productivo como las que ya conoces.

Sin embargo, eso no deja de ser una falsa sensación de seguridad basada en tu (limitada) visión personal. Siempre hay un margen de mejora y una buena forma de conocer alternativas es aprender de otros. Al aprender otros lenguajes de programación te estás exponiendo a una forma diferente de trabajar, con sus partes buenas y sus partes malas, y tal vez descubras ideas que te sorprendan.

Esta exploración de otros ámbitos puede desembocar en que acabes cambiando de lenguaje “principal” hacia alguno de los que has aprendido. Al principio además te parecerá todo maravilloso, porque lo nuevo siempre atrae. Pasados unos años… bueno, el tiempo lo diría.

Lo más probable es que la exploración de otros lenguajes no se traduzca en una migración hacia ellos, pero seguramente puedas obtener nuevas ideas y formas de trabajar que influencien la forma en que utilizas tus herramientas actuales y te permita sacarles más partido. Tal vez un día te descubras evitando las clases en C#, adaptando librerías de ClojureScript a Javascript o incluso utilizando Monoides para configurar tu aplicación de Java.

Me gusta compararlo con viajar. Viajar es una actividad muy enriquecedora porque te permite conocer otras ciudades, otras personas y otra forma de ver la vida. Es poco probable que te mudes a una nueva ciudad sólo porque has estado allí unos días y te ha gustado, pero el viaje en si mismo habrá merecido la pena.

Hay quien piensa que, actualmente, con los lenguajes multiparadigma que permiten utilizar un enfoque funcional, orientado a objetos, procedural, con tipado estático, dinámico o gradual, esto de conocer varios lenguajes pierde sentido. A fin de cuentas, en mi lenguaje favorito ya tengo todo lo que necesito. No soy nada partidario de la featuritis en lenguajes de programación, pero es que además no es comparable. No se trata sólo de una cuestión de paradigmas, es también una cuestión cultural.

Siguiendo con la comparativa de los viajes, es como ir a Las Vegas. Sí, hay una recreación de la Torre Eiffel. Y de los canales de Venecia. Y pirámides, como en Egipto. Pero no es lo mismo, es sólo cartón piedra imitando el original. Incluso aunque la reproducción fuese físicamente perfecta y no pudieras distinguirla del original, lo que hay alrededor no es igual y pierdes la parte de cultura, motivaciones e historia que hay detrás.

Normalmente cuando se habla de alternativas en desarrollo siempre se acaba citado el típico The Right Tool For The Job ™, y este caso no es diferente. Si conoces varios lenguajes de programación, podrás elegir el que mejor se adapte para cada caso y, al menos en teoría, ser más feliz y productivo.

Y siguiendo este hilo de argumentación acabamos, directamente, en el siguiente punto: los proyectos políglotas.

Los peligros del proyecto políglota

Parece razonable. Si somos programadores políglotas, capaces de trabajar con soltura en diferentes lenguajes de programación, qué mejor que aplicar eso en cada proyecto para poder aprovechar al máximo las características de cada lenguaje y poder emplear en cada parte del proyecto la herramienta que más se adecúe a ella.

Es uno de los argumentos a favor de las arquitecturas basadas en servicios (micro o no). Teniendo componentes aislados obtenemos, no sólo flexibilidad en despliegue, sino también flexilibidad en desarrollo y, en concreto, en el lenguaje que queremos utilizar.

Tampoco hace falta irse a arquitecturas complejas. Muchos lenguajes permiten interoperar fácilmente con otros lenguajes. Por ejemplo, mezclar lenguajes dentro de .NET o de la JVM es bastante fácil, y casi todo los lenguajes tienen algún tipo de forma de interoperar con C. Podríamos tener partes de una aplicación desarrollada en C#, usando librerías concretas en F# y no habría problema.

Además, es muy raro encontrarse a día de hoy con un proyecto que no esté usando ya varios lenguajes, aunque sean “auxiliares”. SQL para acceder a una base de datos, lenguajes de marcado (HTML, XAML, etc.) para el interfaz de usuario, Javascript (porque, como sabéis, todo acaba llevando Javascript), algún lenguaje de scripting para automatizar tareas… Podríamos decir que hoy en día casi todos los proyectos son, de alguna forma, políglotas.

Pese a esta realidad y las innegables ventajas de poder usar el lenguaje más adecuado en cada momento, esta vía tiene riestos que hay que evaluar antes de lanzarse a utilizar 12 lenguajes de programación diferentes en el próximo proyecto.

Por una parte, tenemos un problema directo de necesidad de conocimiento. Si el proyecto utiliza varios lenguajes, con distintos estilos de programación, distintas librerías y distintas herramientas, tenemos dos opciones: o tenemos especialistas en cada área, o tenemos gente capaz de manejarse en todas.

La primera opción, tener especialistas en cada área/lenguaje, requiere que el equipo tenga un cierto tamaño como para poder diferencias bien los roles, y disminuye la tolerancia a fallos del equipo porque contribuye a crear silos de información.

La alternativa, contar con gente capaz de manejarse en todos los lenguajes, supone una mayor dificultad para incorporar gente al equipo y para formarla. Incluso teniendo la gente formada, estar haciendo constantemente cambios de contexto al pasar de un lenguaje a otro puede ser un inconveniente (aunque también podría ser un aliciente para evitar la monotonía, todo hay que verlo).

Este problema “humano” es importante, pero existe un problema técnico que, en mi opinión, puede acabar pensado más: el incremento del número de dependencias.

Pocas dependencias hay más fuertes en un proyecto que el lenguaje de programación elegido. A partir del lenguaje irán surgiendo las demás dependencias: herramientas, librerías, gestores de paquetes, etc.

Si trabajas con lenguajes de programación cercanos, que compartan plataforma, habrá dependencias que puedas reutilizar. Por ejemplo, si optas por C# y F#, puedes trabajar con Visual Studio, utilizar NuGet como gestor de paquetes (o, aun mejor, Paket) y ejecutar ambos sobre el CLR. Aun así, para poder explotar al máximo las características de cada lenguaje probablemente acabes con algunas dependencias “duplicadas” para disponer de la solución más idiomática en cada caso.

Si empiezas a desarrollar una web en C#, interactuando con servicios en Go y un frontend en Javascript… bueno, te lo vas a pasar mejor manteniendo runtimes para cada una de las cosas, compiladores para todas y librerías específicas.

Este escenario no tendría por qué ser tan terrible. En un proyecto grande, de larga duración, dedicar tiempo a montar toda esta infraestructura puede compensar con el teórico incremento de productividad asociado a disponer de lenguajes mejores para cada tarea concreta.

Desgraciadamente, cada vez es más difícil ver esto como una inversión inicial, y el ritmo al que avanza todo hace que sea complicado mantener el montaje inicial mucho tiempo. Tendrás que mantener actualizadas las librerías de cada lenguaje. Adecuarte a los cambios de versión de sus plataformas. A las nuevas herramientas y formas de trabajo que se hayan puesto de moda y que, si quieres usar las versiones nuevas de las librerías, no te queda más remedio que usar.

Todo esto suele ser, en el sentido más estricto de la expresión, una pérdida de tiempo. Son cosas que aportan muy poco a los usuarios de tu aplicación, pero que no te queda más remedio que hacer. Si en lugar de un lenguaje y una plataforma tienes 3 o 4, la pérdida de tiempo se multiplica, por lo que es fundamental estar seguro de que las ventajas que obtienes por utilizar varios lenguajes, compensa este incremento de tiempo de mantenimiento.

Conclusión

Nunca hay que dejar de viajar, digo, de conocer otros lenguajes de programación. Aparte de que suele ser divertido, es una forma muy buena de aprender cosas diferentes que puedes luego trasladar a tu día a día. Como mínimo, te permitirá afrontar los problemas con más herramientas y otros puntos de vista, y eso ya es positivo per se.

Además, este mundo cambia muy rápido y nunca se sabe si uno de esos lenguajes de programación que hoy aprendes por curiosidad se acabará convirtiendo en tu próxima oportunidad profesional (o en tu salvación cuando tu lenguaje favorito caiga en el olvido).

Con lo que tenemos que tener cuidado es con abusar de ese nuevo conocimiento e intentar mezclar muchos lenguajes de programación en un mismo proyecto. Es algo que puede resultar tremendamente útil y productivo, pero que tiene unos riesgos y costes asociados que hay que considerar cuidadosamente antes de lanzarse a ello.

Posts relacionados:

  1. Productos y Proyectos y Viceversa

Variable not found: Enlaces interesantes 272

$
0
0
Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.NET

ASP.NET

.NET Core / ASP.NET Core

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Otros

Componentes/bibliotecas

  • RxDB
    Client-Side Database for Browsers, NodeJS, electron, cordova, react-native and every other javascript-runtime
  • TwitterBootstrapMVC
    Development for ASP.NET MVC with Bootstrap made simple
Y para terminar, ahí va una imagen curiosa del uso creativo de la recursividad en el mundo real, publicada por un usuario de Reddit:

Tarjeta con instrucciones de uso donde aparece la propia tarjeta

Publicado en Variable not found

Bitácora de Javier Gutiérrez Chamorro (Guti): La evolución de FileOptimizer

$
0
0

En FileOptimizer y los logotipos más populares, vimos las mejoras reales que obtiene FileOptimizer. Además, con cierta regularidad, os voy manteniendo al día sobre su evolución, y os voy contando algunos secretillos. En el artículo de hoy, vamos a ver como ha mejorado el grado de optimización que consigue FileOptimizer. Aunque la primera versión fue […]

La entrada La evolución de FileOptimizer aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

Variable not found: Solucionar error "Connection string no puede ser NULL ni estar vacío" al publicar WebJobs en Azure desde VS2015

$
0
0
Publish Azure WebJobDesde hace algún tiempo estoy sufriendo un error que aparece justo en el momento de publicar un Webjob a Azure desde Visual Studio 2015.

Supongo que tendrá su motivo y se deberá a que estoy haciendo algo mal, pero bueno, el caso es que el error es un poco molesto porque impide la publicación del proyecto y, aunque no es difícil de solucionar, siempre me obliga a perder unos minutos en buscar una solución y aplicarla a mi proyecto.

El error que podemos ver en la ventana de resultados es el siguiente:
Error : El argumento 'DefaultConnection-Web.config Connection String' no puede ser NULL ni estar vacío.
O su versión en inglés:
Error : The 'DefaultConnection-Web.config Connection String' argument cannot be null or empty.
Así que, a modo de nota mental, y si acaso poder echar una mano a alguno que os encontréis ante el mismo escenario, comento dos soluciones que me han funcionado bien. Simplemente elegid la que más os convenza.

Solución 1

Es la más sencilla, y consiste únicamente en eliminar la carpeta /obj del proyecto en cuestión. Tras ello, volvemos a publicar y funcionará todo bien.

Solución 2

La segunda solución es algo más compleja, aunque tampoco para echarse las manos a la cabeza.

El problema está en el archivo de publicación (.pubxml) que estáis usando para publicar el WebJob, que por algún motivo ha perdido la cadena de conexión por defecto del proyecto. Podéis encontrarlo en la carpeta /Properties y tiene una sección como la siguiente:
<PublishDatabaseSettings>
<Objects>
<ObjectGroup Name="DefaultConnection" Order="1" Enabled="False" xmlns="">
<Destination Path="" />
<Object Type="DbCodeFirst">
<Source Path="DBMigration"
           DbContext="MyProject.MyDataContext, MyProject"
           MigrationConfiguration="MyProject.Migrations.Configuration, MyProject"
           Origin="Convention" />
</Object>
</ObjectGroup>
</Objects>
</PublishDatabaseSettings>
Lo único que hay que hacer para poder publicar sin problema es transformar la cuarta línea (el tag <Destination>) de la siguiente forma:
<PublishDatabaseSettings>
<Objects>
<ObjectGroup Name="DefaultConnection" Order="1" Enabled="False" xmlns="">
<Destination Path="{deployment connection string}" />
<Object Type="DbCodeFirst">
<Source Path="DBMigration"
           DbContext="MyProject.MyDataContext, MyProject"
           MigrationConfiguration="MyProject.Migrations.Configuration, MyProject"
           Origin="Convention" />
</Object>
</ObjectGroup>
</Objects>
</PublishDatabaseSettings>
Es decir, establecemos el atributo Path del tag <Destination> al valor "{deployment connection string}" y lo tendremos solucionado.

¡Y eso es todo! En fin, este post trata sobre esos misterios que suceden de vez en cuando y nos hacen dedicar minutos de nuestro preciado tiempo a labores de fontanería. Espero que lo descrito aquí os sea de utilidad en algún momento... o no, porque significaría que no se os ha dado nunca este caso, y eso no es mala cosa ;)

Publicado en Variable not found.

Picando Código: Riff Studio – Aplicación Android para practicar música

$
0
0

Riff StudioSin duda las mejores aplicaciones son las que surgen a partir de una necesidad. Es el caso de Riff Studio, una aplicación para dispositivos Android hecha por y para músicos desarrollada por Bruno Azzinari. Su objetivo es facilitarnos practicar canciones.

Básicamente tenemos que armar una lista de temas agregándolos desde nuestro dispositivo. Desde ahí podemos reproducirlos y cambiarles el tono y velocidad.

Podemos bajarle la velocidad a una canción para practicarla e ir aumentando de a poco a medida que vaya saliendo mejor. Para práctica más intensa podemos incluso subirle la velocidad a una canción. También cambiar el tono si usamos una afinación distinta o para usa un registro distinto si estamos cantando.

Todos estos cambios se pueden hacer en tiempo real afectando a la reproducción en el momento y son independientes. El tono se puede cambiar en semitonos y la velocidad en porcentaje de la original.

Una de las características interesantes que se agregó en la última actualización es la de bucle. Podemos definir un segmento de la canción que queremos que se repita en un bucle. Esto es súper útil y personalmente lo he usado para sacar algún punteo complicado en la guitarra. Al poder repetir el mismo segmento y a una velocidad más lenta, no se nos escapa ni una nota.

Podemos exportar en formato mp3 la canción modificada para reproducirla desde cualquier otro reproductor digital y no depender del programa.

La interfaz es muy sencilla y cómoda de usar. El desarrollador está abierto a críticas, sugerencias y feedback en general, y lo pueden contactar en el mail brazzilabs@gmail.com con ideas.

RiffStudio en el Google Play Store

[See image gallery at picandocodigo.net]

Poesía Binaria: Edición de vídeo en GNU/Linux con software libre, ¿Qué características necesito para editar vídeo?

$
0
0

El momento ha llegado, tras tantos años sin pensar en ella. He vuelto a encontrármela, ahí de pie, delante de mí. Su nombre es Pandora y con una risa maliciosa viene a este humilde blog. A llenar nuestras almas de oscuridad aumentando el contraste y corrigiendo el color.

Aunque poco a poco he estado soltando pinceladas en el tiempo sobre imagen digital en GNU/Linux, vídeo digital, integración de algunas técnicas y demás. Pero creo que ha llegado el momento de hacer algo más grande y preparar una referencia mayor sobre software libre y edición de vídeo.

Mi experiencia personal y privativa

Hace muchos años que me adentré en el mundo de la edición de vídeo por ordenador. Como muchos de nosotros, empecé haciendo pequeñas animaciones en un modesto equipo del siglo pasado, sólo por hobbie y por hacer algo con mi tiempo (en aquella época en la que aún podía gozar de algo de tiempo libre). El programa que solía utilizar era Autodesk Animator. Estaba muy limitado, pero podías hacer muchas cosas. Era muy intuitivo.

Con el tiempo, fui conociendo otros programas de edición de vídeo, hasta que gracias a un amigo conocí el paquete de Ulead Videostudio. Esta persona se compró una tarjeta capturadora en aquella época en que las tarjetas de este tipo ocupaban todo el ancho de la torre y costaban un ojo de la cara y medio riñón… pero venían con mucho software para utilizar. Unos años más tarde fueron bajando de precio, aunque empezaron a ser un poco peores en la captura. Como los ordenadores de aquella época no eran tan rápidos, estas tarjetas solían llevar un chip compresor/descompresor MPEG integrado por lo que a medida que capturabas vídeo lo ibas recibiendo comprimido y todo… y como en todo, un mejor compresor generaba vídeos de menor tamaño con mayor calidad, y dado que el ancho de banda del bus es limitado llegaba un punto que una tarjeta capturadora barata no podía obtener mucha calidad. Con el tiempo terminé comprando una tarjeta de estas, de las baratas, no de las profesionales, aunque dentro de la gama de las baratas me fue muy bien.

Unos años más adelante, con capturadora nueva, no tenía cámara de vídeo y me pasé mucho tiempo pidiendo cámaras de vídeo. Si algún amigo tenía una cámara de vídeo, allá que iba yo cuando tenía algún proyecto para grabar. Me lo pasaba en grande montando historias para hacer pequeñas grabaciones en mi adolescencia. ¿Algunos ejemplos? (He perdido mucho material, tenía muchas cosas grabadas en CD y esos CDs cambiaron de plano existencial, fue una gran pérdida, no es lo mejor, pero sí lo que pude rescatar y lo que Youtube no me ha quitado por tema de licencias).

He perdido muchísimo material. Además, en 2003 hice mi paso definitivo a GNU/Linux. Aunque tenía la espinita del vídeo digital, me pudo más el hecho de dejar Windows de lado y llegó un punto en el que no arrancaba Windows para nada por lo que GNU/Linux pasó a ser el único sistema operativo de mi ordenador, y lo ha sido hasta el día de hoy.

Bueno, antes de nada decir que hasta 2003 más o menos, los programas de edición de vídeo para Windows tampoco destacaban por su estabilidad. Ya fuera por el propio sistema operativo, el uso intensivo de la memoria, los codecs y la complejidad de los sistemas, era común encontrarse con algún pantallazo azul de vez en cuando, o que el propio programa en el que estabas editando vídeo en el momento de más concentración se cierra inesperadamente dejándote todo el trabajo a medias. O, como me pasó con Huyendo del destino, el ordenador se reinició y cuando todo arrancó vi que los principales archivos del proyecto estaban corruptos (era mucho más largo y abandoné el proyecto).

Lo sé, había editores de vídeo para GNU/Linux

Soy consciente de ello, es más, de vez en cuando abría alguno e intentaba hacerme con él, aunque el problema, como en Windows era la estabilidad, aunque en este sistema era aún peor. Por un lado, los programas de edición no eran lo suficientemente maduros, casi todos acababan de nacer (incluyo como mucho hasta 2004, avisadme si falta alguno):

  • KDEnlive nació en 2002
  • Cinelerra nace en 2002
  • PiTiVi nace en 2003
  • Blender se libera en 2002, aunque no editaba vídeos
  • Shotcut nace en 2004
  • LiVES nace en 2003

Y es normal que tratándose de software libre, en este sentido estos desarrollos fueran bastante lentos. Además estábamos en una época cambiante, con respecto a codificación de vídeo (MPEG-4 estaba pisando fuerte, había nuevas formas de codificar audio y surgían formatos propios de Microsoft o Apple a los que había que dar soporte. También estaba entrando el DVD y los usuarios de GNU/Linux teníamos derecho a utilizar el DVD). Además, las cámaras empezaban a utilizar el IEEE1394 que era más rápido que USB para transmisión de datos… En definitiva, los desarrollos de codificación y soporte cambiaban muy a menudo, y quienes desarrollaban estos programas de vídeo al no estar amparados por ninguna empresa, encima recién nacidos los proyectos, era normal que no fuera la cosa tan estable como debería… y ya por no meternos en el tema de tarjetas gráficas.

El caso es que para 2006 pude volver a hacer algún montaje sencillo, con una estabilidad que más o menos me permitía no arrancarme el pelo de desesperación. En este caso con KDEnlive, un editor que he utilizado mucho y durante muchas horas, que me ha dado alegrías y penas y del que alguna vez he hecho algo en este blog.

También quiero dejar claro que, la industria del cine, según dice empieza a utilizar GNU/Linux en el año 2002 o incluso antes. Empresas como Industrial Light and Magic, Rhythm and Hues, Dreamworks, WetaDigital o Pixar dicen que utilizan GNU/Linux en algunos de sus procesos, eso sí con herramientas propias y/o privativas.

Estado del arte

Pero llega 2017. Y, sobre todo me animo a hacer esta serie de posts el hecho de que KDEnlive, después de algo más de dos años sin usarlo, en diciembre de 2016, lo he notado más estable que nunca, pude hacer un vídeo sin que me diera guerra el programa, incluso abrí un montaje hecho hace casi 5 años y no tuve ningún problema, bueno sólo un filtro que no estaba, pero era culpa mía que no lo instalé.
Por otro lado, Blender, que, aunque me gusta, tengo que decir que no siempre las versiones estables son estables, ni están libres de fallos tontos (los fallos ocurren, lo sabemos, pero hay algunos más tontos que otros, y a veces la mezcla de dos imágenes no funciona bien, o se ve claramente que se desborda un número y no se ha controlado un valor (lo siento, mi faceta de programador siempre está alerta). Pero abrí Blender 2.78 y me dio muy buena sensación, iba muy ligero y no se quejó para un pequeño montaje (cosa que antes sí que hacía).

Así que, quiero empezar este año a hacer un pequeño análisis y empezar a realizar tutoriales sobre edición de vídeo digital en GNU/Linux, efectos especiales, composición, etc. Es cierto que el software privativo en este sentido está a muchos años de distancia, pero vamos por buen camino, incluso hay cosas que podremos hacer con software libre y no con software privativo, aunque nos toque tener que programarlas.

Un pequeño vídeo

Digo edición de vídeo, pero no profundizo

Es un tema complejo y amplio que he metido en edición de vídeo, aunque sé que hay muchos subtemas que debemos tratar. Una cosa es el montaje, y otra la composición (aunque hay herramientas que las integran). Por otro lado tenemos la conversión, recorte, etalonaje (palabro de la vieja escuela, relativo a la corrección y tratamiento del color), análisis espacio-temporal, screencasting, stop-motion, edición y renderizado 3D, edición de audio y de imagen fija (sí, utilizaremos GIMP para preparar cosas), y mucho más. Tenemos para largo.
Y todo eso sin entrar en temas de producción donde encontramos software para creación de guiones, presupuestos, gestión de proyectos, storyboarding y muchísimo más.

¿Qué le pido a un editor de vídeo?

Hablo aquí de editores de vídeo no lineales, de los de toda la vida, para hacer montajes, en los que cogemos vídeos de origen, aplicamos un efecto y obtenemos un vídeo resultante.

Me quiero centrar en tres características:

Edición multicapa

Esto es, que podamos superponer varias capas de vídeo o imagen unas encima de otras para obtener una imagen resultante mezcla de todas. Podremos aplicar filtros a algunas imágenes intermedias, introducir transparencias, mover capas dentro de la imagen y mucho más.
Casi todos los editores suelen tener esta característica, mejor o peor, sólo quiero quitar del medio los programas que no valen para este tipo de edición, los que dan muchos problemas al realizarlo. No está bien que el día que necesitas 5 capas para montar un vídeo el programa empiece a ir extremadamente lento.

Proxys

Ahora editamos en resoluciones muy grandes. Es más, en el año 2000 se editaban algunas películas en 8K (y eso que ahora estamos con la novedad de las pantallas 4K). Eso sí mientras estamos editando, durante casi todo el proceso no nos importa tener las imágenes a la mayor resolución. Para conocer cambios de escena, hacer muchas tareas de corrección de color o mezclado no necesitamos las imágenes en alta resolución.
Así que un buen programa de edición de vídeo, aunque ocupe algo más de disco duro, y tarde un poco más al principio, nos permitirá generar vídeos en resoluciones más pequeñas y con algoritmos de fácil descompresión con el objetivo de facilitarnos el trabajo.
Imaginemos que mi cámara digital comprime los vídeos en MPEG-4 AVC (H.264, mal empezamos si queremos ser libres, pero bueno). El formato crea archivos de pequeño tamaño con nuestro vídeo, lo malo es que necesitamos un uso intensivo de CPU, incluso es más fácil reproducir un vídeo que extraer un fotograma suelto, y cuando estamos editando casi siempre estaremos extrayendo esos fotogramas por separado para realizar todo el trabajo de las capas. Muchas veces estos proxys suelen hacerse en MPEG-2, que a estas alturas se descomprime de forma muy sencilla, o en imágenes sueltas, JPEG, TIFF, PNG…). En definitiva, a lo mejor perdemos mucho tiempo en este trabajo. Además, si estamos trabajando en FullHD (1920×1080), seguramente la vista previa esté a un cuarto de resolución (960×540) o menos, por lo que, para crear las vistas previas, no merece la pena hacer todo el proceso en resolución completa, podemos coger la imagen a 960×540 y trabajar con ella, el resultado será el mismo y se harán un 25% de operaciones, lo que nos permitirá hacer cosas más complejas.

Eso sí, cuando vayamos a hacer el render definitivo, el programa cogerá los archivos originales. En hacer el render, no nos podemos precipitar. Es verdad que podremos optimizar y hay programas más rápidos que otros, pero lo realmente importante, creo que es el proceso de edición, el render siempre puede tirarse un ordenador varios días encendido, o podemos hacerlo en un servidor.

Máscaras

Esto es más de composición, pero me encanta que los editores tengan la posibilidad de incluir máscaras rápidas para hacer pequeños efectos rápidos. Con estas máscaras podremos dibujar zonas de la imagen que serán relevantes para nosotros descartando lo demás, que luego podremos superponer y hacer muchas tareas con ellas. Las máscaras deberían poder animarse, con lo que crearemos efectos de rotoscopía. Creo que hablaré sobre esto en un futuro post. Es un tema que me encanta.

¿Algo más?

Hay muchas más cosas que un editor debe tener, pero empecemos por estas tres. Por supuesto, deberíamos poder aplicar filtros de color, y corregirlo de forma precisa, seleccionar áreas que queremos con diferente color… aplicar efectos de barrido, ajustar velocidad de vídeos y muchas más cosas a las que me gustaría dedicarle algunos futuros posts y vídeos.

Programas para abrir boca

Sigue este enlace, donde veremos una colección de programas y recursos con los que editar vídeo y audio en GNU/Linux. El listado irá creciendo con enlaces a manuales, tutoriales y guías poco a poco.

Y tú, ¿qué software usas?

¿Qué software utilizas tú para tus montajes de vídeo? ¿Qué características son las imprescindibles para ti?

The post Edición de vídeo en GNU/Linux con software libre, ¿Qué características necesito para editar vídeo? appeared first on Poesía Binaria.

Blog Bitix: Ejemplo de apagado y encendido de diodo LED con la Raspberry Pi en Java

$
0
0
Raspberry Pi
Java

El primer ejemplo que haré de un programa Java que usa los pines GPIO para realizar algo con el kit de inicialización a la electrónica con la Raspberry Pi consiste en un pequeño programa Java que hace parpadear un diodo LED. La librería Diozero ofrece a los programas Java el acceso a los pines GPIO de la diferentes versiones de la Raspberry Pi desde la 1 (rev 1 y rev 2) pasando por los modelos B+, 2 y 3. Otra librería que se puede usar con el lenguaje de programación Java es Pi4J aunque personalmente Diozero me ha gustado más por ser de más alto nivel.

Algunas de las características que ofrece la librería Diozero son:

Una de las primeras cosas a conocer es como se numeran los pines en la Raspberry Pi ya que hay varias nomenclaturas (header, wiringPi y Broadcom) y que nomenclatura utiliza la librería Diozero. También deberemos tener en cuenta el modelo de la Raspberry Pi que poseamos ya que según el modelo hay pequeñas diferencias en algunos pines. Además si usamos una placa de extensión para pruebas sin sodadura como la wiringPi deberemos identificarlos por su nombre. Yo que poseo una de las primeras Raspberry Pi (la 1, rev1) el correspondiente su esquema de pines Raspberry Pi 1 (rev. 1) es el del enlace. En ese esquema se define que el pin número 12 según el conteo del header corresponde a GPIO 18 según la nomenclatura Broadcom y la librería Diozero y al GPIO 1 en la librería Pi4J y en la placa de extensión wiringPi.

Placas modelos Raspberry Pi 1 B y 3 B
Breadboard y placa extesión GPIO wiringPi de 26 pines y 40 pines

Como uso la placa de extensión wiringPi para conectar los pines de la Raspberry Pi a otra placa para hacer pruebas sin soldadura también conocidas como breadboard resultará que en el programa Java al usar Diozero uso la nomenclatura Broadcom para identificar los pines pero al conectar los cables en la placa de pruebas uso la nomenclatura de wiringPi.

Para el ejemplo utilizaré la placa de extensión sin soldadura, una resistencia de 200 ohmios (dadas sus bandas de colores rojo, negro, marrón y dorado) y un diodo LED además de un par de cables macho-macho para realizar las conexiones electrónicas entre el GPIO 18 (según la nomenclatura de la librería Diozero y Broadcom, 12 según la nomenclatura del header y 1 según la de wiringPi) y la resistencia además de entre el diodo y la línea de tierra. Los diodos LED poseen una orientación y hay que conectar la resistencia con el polo positivo del diodo LED, el polo positivo del diodo LED identifica porque es la patita larga y el negativo con tierra es la patita corta. Si realizamos la conexión al revés solo pasará que el diodo no se enciende pero no lo estropeará, la resistencia si es necesaria para no hacer que pase por el diodo una intensidad que lo estropee como se explica en ¿Qué resistencia ooner a un LED?.

Diodo blanco y resistencia de 200 ohmios

Las resistencias poseen cuatro bandas de colores que indican el valor en ohmios de esa resistencia, la tabla de colores es el siguiente:

Código de colores de las resistencias

El programa Java para hacer parpadear el diodo LED con la librería Diozero con el proveedor pigpio. El ejemplo consiste en activar y apagar el pin sucesivamente en un bucle y usar el método Thread.sleep para que pase unos segundos entre uno y otro y nos de tiempo a ver el encendido y apagado. El nada complejo programa Java para controlar el diodo y un vídeo de su funcionamiento están a continuación.

En un artículo anterior comento como disponer de un entorno para desarrollar, desplegar las librerías jar en la Raspberry Pi y como ejecutar los ejemplos desde la línea de comandos usando una combinación de herramientas de SSH, rsync y Ansible.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew executeBlink.

Jesús Perales: Iniciar un contenedor de docker automaticamente al reiniciar

$
0
0
Iniciar un contenedor de docker automaticamente al reiniciar

Es posible iniciar imagenes de Docker de una forma muy sencilla con simplemente ejecutar un docker start contenedor, pero al tener un servicio ya en un servidor productivo al reiniciarse vamos a querer que este inicie automaticamente.

Para hacer esto con systemd(algunos lo odian, otros lo aman y otros tantos simplemente trabajan con el) es necesario crear un archivo y enviarlo a la carpeta /etc/systemd/system/ y habilitarlo.

Aquí muestro un ejemplo con un contenedor llamado nexus

Contenido del archivo :

[Unit]
Description=Nexus container
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStart=/usr/bin/docker start -a nexus
ExecStop=/usr/bin/docker stop -t 2 nexus

[Install]
WantedBy=default.target

Comandos a ejecutar:

Creamos el archivo:

touch docker-nexus.service

Lo abrimos con nano y pegamos el contenido de arriba ya personalizado:

nano docker-nexus.service

copiamos el archivo a la carpeta especial de systemd:

sudo cp docker-nexus.service /etc/systemd/system/

Lo habilitamos:

systemctl enable docker-nexus.service

Al reiniciar nuestor SO debería de levantarse automaticamente nuestro contenedor.

Fuentes:


Poesía Binaria: Aplicaciones web sin servidor, o casi (arquitecturas serverless)

$
0
0


¿Cómo es esto posible? Si tenemos una cosa clara en el mundo de Internet es que si queremos tener en pie un servicio, éste debe estar alojado en una máquina que esté conectada a Internet, con energía y que nos asegure en cierta forma que está levantada. Estos servidores pueden ser de diversos tipos según las necesidades de la aplicación: web, base de datos, autentificación, tareas, logs, notificaciones, etc. Así que, ¿qué es esto de aplicaciones sin servidor? ¿o serverless?

¿Sin servidor? Serverless?

En realidad, este término no tiene que ver con que exista físicamente un servidor o no, sino con el hecho de delegar su administración a un tercero. Al mismo tiempo, nosotros no pedimos un servidor, pedimos un servicio Por lo que este término se aplica siempre que no tengamos que administrar el servidor que ejecuta un servicio, es más, en muchos casos, no sabemos sus características ni tenemos acceso a él más allá del servicio que ejecuta. Es más, el proveedor nos da un punto de entrada para poder llamar al servicio, y lo que haya por detrás será todo un misterio. Ellos nos garantizan el servicio encargándose de todo.

Esto pueden ser aplicaciones en la nube (ay la nube, ese gran desconocido, literalmente), como bases de datos, servidores de autentificación, incluso APIs para nuestras aplicaciones. Esto se utiliza sobre todo para aplicaciones móviles, en las que disponemos de un frontend y por detrás se hacen llamadas a servicios web que a su vez pueden hacer llamadas a otros servicios, todos remotos. Esto se suele llamar BaaS (Backend as a Service).

Por otro lado, algo que está muy de moda últimamente es la ejecución de funciones como servicio o FaaS (Functions as a Service), de hecho casi siempre que escuchemos algo sobre Serverless se referirán a esto y no es más que el alojamiento y ejecución de código por un tercero en contenedores de cómputo descentralizados y administrados por un tercero. Estos terceros suelen ser IBM, Google, Microsoft, Amazon, etc En definitiva, nuestro código no sabemos dónde se aloja ni dónde se ejecuta, sólo sabemos cómo llamarlo y cómo ver la respuesta de la ejecución. Es más, dependiendo del uso que hagamos de esta tecnología puede que haya muchas máquinas ejecutando nuestro código a la vez, sirviendo respuestas para nuestras aplicaciones… y todo sin tener que montar ni contratar un servidor.

Como hemos podido ver, estas soluciones son administradas, es decir, tampoco necesitamos que nadie se tome molestas para encargarse de la seguridad del servidor, balanceo de carga, actualizaciones de software, direccionamiento, almacenamiento, ni nada. Sólo debemos encargarnos de subir nuestro código y listo. Pero claro, eso de Serverless, seguro que se le ocurrió a uno de marketing porque suena muy bien. Está claro que servidor sí que hay, pero bueno, no tenemos que prestarle mucha atención.

¿Para qué quiero yo esto?

El objetivo, además de que es algo muy moderno, por un lado es que se reducen costes. Aunque hay que mirarlo con lupa y estudiar casos concretos. Es decir, para una empresa, el tiempo de un empleado supone dinero y el tiempo que el empleado invierte en tareas de mantenimiento de un servidor, significa una inversión económica. Además, tener una máquina contratada para realizar un servicio determinado es también una inversión económica, generalmente en forma de alquiler mensual y esto cubre momentos en los que se está utilizando el servicio y momentos en los que no hay nada utilizándose. Y normalmente, las empresas que facilitan este tipo de servicios cobran por el tiempo de uso de los servicios.

Por otro lado, nos garantiza la escalabilidad de los servicios (escalabilidad? Introducción a la escalabilidad de aplicaciones web. Técnicas, opciones, seguridad y consejos.). Ya que, en el caso de FaaS, sólo ejecutamos funciones, estas funciones podrán conectar con bases de datos y utilizar servicios externos, pero gozan de independencia de la máquina donde se ejecutan por lo que podremos llamar al servicio las veces que necesitemos, simultáneamente y no debemos tener ningún problema en la disponibilidad (no es plan de hacermos un DDOS tampoco). En el caso de BaaS también estamos llamando a servicios independientes por lo que el escalado no es un problema.
El caso es que el tema del escalado, que bien nos puede suponer un quebradero de cabeza de esta forma es transparente a nosotros y lo harán los servicios de manera natural.

Paranoico de mí

Pero claro, estamos dependiendo de un tercero, o de varios terceros, no tenemos control sobre la infraestructura, en muchos casos ni podemos visitarla, internamente no sabemos cómo funciona (más allá de lo que nos cuentan), puede que hasta desconozcamos la versión exacta del software que ejecutan estos contenedores. Aunque es verdad que las empresas muestran gran transparencia en sus operaciones no es lo mismo que si lo hicieras tú.
Siguiendo con la dependencia, la supervivencia de nuestras aplicaciones depende por completo de los servicios del tercero que contratamos. Eso quiere decir que si la empresa cambia sus condiciones (empeorándolas) tenemos un problema, si hacen una actualización de software y nuestro código no es compatible, tenemos otro problema gordo, si la empresa cierra (recordemos que todos los imperios han caído tarde o temprano), tenemos un problema más gordo aún y cambiar de empresa puede ser otro problema si no lo abordamos desde el principio.

No es lo mismo contratar un VPS con un sistema operativo donde podemos copiar los archivos de nuestra aplicación y desplegar (más o menos) que si tenemos que realizar cambios en nuestra aplicación para hacer llamadas a los servicios que hemos cambiado de empresa. Normalmente cada una tiene sus APIs para garantizar los accesos a sus servicios, autentificación de las llamadas, disparadores, etc. Aunque todos suelen tener partes comunes, también tienen sus particularidades.

¿Para qué lo podemos usar?

Lo primero, para tener todos los bloques de nuestra aplicación de Internet separados y bien diferenciados, lo que nos ayudará a crecer más rápidamente y como dijimos antes, la escalabilidad será algo natural.

Aunque ahora me voy a centrar un poco más en FaaS. Es decir, lo que haremos con este tipo de servicio es simplemente la ejecución de código. Una función, por ejemplo hecha en Python que tiene que ejecutarse en alguna ocasión. ¿Cuándo? Generalmente estas funciones se ejecutarán cuando se dispare un evento del tipo: petición web, entrada de datos en base de datos, inserción de mensaje en un log, creación de una notificación, entrada de un usuario determinado, nuevos archivos en servidores de almacenamiento y mucho más.

Normalmente nuestra aplicación tendrá un servidor web de entrada, que sí lo gestionamos nosotros (ese es nuestro VPS, o nuestros servidores balanceados), pero ahí vamos a incluir el código más básico posible: generación de páginas a partir de las plantillas, obtención sencilla de datos, control de sesiones, y poco más.

Por ejemplo, si tenemos en base de datos toda la información de ventas anual y queremos crear una gráfica resumen con todo y algunos datos estadísticos, seguramente si hemos vendido mucho debemos hacer cientos de cálculos y podemos encargárselos a otro liberando algo de carga en nuestro servidor, aunque el la entrada/salida de nuestra base de datos va a tener impacto igual, pero los cálculos y el trabajo de memoria y CPU para extraer medias, periodos, tendencias y demás se lo lleva otro. Todo esto lo podemos extender a todas aquellas tareas que sean un poco más pesadas de la cuenta. A lo mejor no tardan mucho tiempo, pero involucran sobre todo CPU y memoria y puede perjudicar el rendimiento global de nuestra aplicación. Estas tareas pueden ser: grandes importaciones/exportaciones de datos, generación de imágenes o miniaturas, generación de vídeos, etc.

En el lado del programador

El número de lenguajes es limitado y lo pone el proveedor del servicio. Contamos con lenguajes más o menos famosos y útiles como Python o NodeJs, en algunos sistemas podremos ejecutar Java o C#. Es normal que todos los lenguajes no estén soportados, porque hay que preparar los entornos de ejecución para que sea seguro utilizarlos y, supongo que con el tiempo se podrá ampliar el número de lenguajes soportados. Esto también puede causar dependencias, ya que puede que un proveedor soporte un lenguaje y otro no.

Por el lado de los desarrolladores, tenemos que contar con herramientas que nos permitan desarrollar el código de estos servicios de forma sencilla. Como la forma más común y sencilla de disparar los eventos es con peticiones HTTP. Actualmente hay sistemas que nos permiten crear un pequeño servidor local que atiende estos tipos de peticiones y ejecuta el código seleccionado. Luego, cuando pasemos el código a nuestro servidor sólo tendremos que cambiar el punto de acceso al servicio (que no será local, será el que nos dé nuestro proveedor). Y todo porque desarrollar desplegando todo el rato es muy feo.

Es conveniente también automatizar los despliegues. Y tenemos herramientas para ello, pero dependen del proveedor que cojamos. Sólo tendremos que crear un script o un archivo de instrucciones de despliegue y ya podremos usarlo para desplegar. Lo bueno es que esta tecnología nos permite crear tantos eventos como queramos, y tenemos la posibilidad de tener varios entornos, testing y producción por ejemplo, y sólo tenemos que cambiar el punto de entrada.

Tenemos que tener en cuenta que las funciones que ejecutamos deben estar lo más optimizadas posible, porque nos cobrarán por tiempo de ejecución, por lo tanto, las funciones deben estar bien definidas, tener un único objetivo y evitar entrar en bucles infinitos o esperas innecesarias. Eso sí, estas funciones podrán disparar eventos que ejecuten otras funciones en el futuro, así que tendremos que tener un poco de cuidado para que nuestras funciones no se pisen las unas a las otras…

Resumiendo

Para no agobiarnos mucho, con esta tecnología, definiremos una serie de eventos que, cuando se disparen, ejecutarán un fragmento de código en un ambiente de ejecución o contenedor controlado. Este código se ejecutará y el contenedor se destruirá. Si la función tiene que ejecutarse muchas veces, incluso simultáneamente, el proveedor se encarga del escalado/encolado/etc.

Estas funciones pueden conectar con otros servicios de base de datos, almacenamiento u otros tipos de servidores, incluso disparar la ejecución de otras funciones.

En próximos posts voy a contaros mi experiencia trabajando con Amazon Lambda

Referencias

No te pierdas estos sitios con interesantes lecturas sobre arquitectura Serverless:

The post Aplicaciones web sin servidor, o casi (arquitecturas serverless) appeared first on Poesía Binaria.

Blog Bitix: Ejemplo encender y apagar diodo LED con la Raspberry Pi en Java

$
0
0
Raspberry Pi
Java

El primer ejemplo que haré de un programa Java que usa los pines GPIO para realizar algo con el kit de inicialización a la electrónica con la Raspberry Pi consiste en un pequeño programa Java que hace parpadear un diodo LED. La librería Diozero ofrece a los programas Java el acceso a los pines GPIO de la diferentes versiones de la Raspberry Pi desde la 1 (rev 1 y rev 2) pasando por los modelos B+, 2 y 3. Otra librería que se puede usar con el lenguaje de programación Java es Pi4J aunque personalmente Diozero me ha gustado más por ser de más alto nivel.

Algunas de las características que ofrece la librería Diozero son:

Una de las primeras cosas a conocer es como se numeran los pines en la Raspberry Pi ya que hay varias nomenclaturas (header, wiringPi y Broadcom) y que nomenclatura utiliza la librería Diozero. También deberemos tener en cuenta el modelo de la Raspberry Pi que poseamos ya que según el modelo hay pequeñas diferencias en algunos pines. Además si usamos una placa de extensión para pruebas sin sodadura como la wiringPi deberemos identificarlos por su nombre. Yo que poseo una de las primeras Raspberry Pi (la 1, rev1) el correspondiente su esquema de pines Raspberry Pi 1 (rev. 1) es el del enlace. En ese esquema se define que el pin número 12 según el conteo del header corresponde a GPIO 18 según la nomenclatura Broadcom y la librería Diozero y al GPIO 1 en la librería Pi4J y en la placa de extensión wiringPi.

Placas modelos Raspberry Pi 1 B y 3 B
Breadboard y placa extesión GPIO wiringPi de 26 pines y 40 pines
Esquema del cableado

Como uso la placa de extensión wiringPi para conectar los pines de la Raspberry Pi a otra placa para hacer pruebas sin soldadura también conocidas como breadboard resultará que en el programa Java al usar Diozero uso la nomenclatura Broadcom para identificar los pines pero al conectar los cables en la placa de pruebas uso la nomenclatura de wiringPi.

Para el ejemplo utilizaré la placa de extensión sin soldadura, una resistencia de 200 ohmios (dadas sus bandas de colores rojo, negro, marrón y dorado) y un diodo LED además de un par de cables macho-macho para realizar las conexiones electrónicas entre el GPIO 18 (según la nomenclatura de la librería Diozero y Broadcom, 12 según la nomenclatura del header y 1 según la de wiringPi) y la resistencia además de entre el diodo y la línea de tierra. Los diodos LED poseen una orientación y hay que conectar la resistencia con el polo positivo del diodo LED, el polo positivo del diodo LED identifica porque es la patita larga y el negativo con tierra es la patita corta. Si realizamos la conexión al revés solo pasará que el diodo no se enciende pero no lo estropeará, la resistencia si es necesaria para no hacer que pase por el diodo una intensidad que lo estropee como se explica en ¿Qué resistencia ooner a un LED?.

Diodo blanco y resistencia de 200 ohmios

Las resistencias poseen cuatro bandas de colores que indican el valor en ohmios de esa resistencia, la tabla de colores es el siguiente:

Código de colores de las resistencias

El programa Java para hacer parpadear el diodo LED con la librería Diozero con el proveedor pigpio. El ejemplo consiste en activar y apagar el pin sucesivamente en un bucle y usar el método Thread.sleep para que pase unos segundos entre uno y otro y nos de tiempo a ver el encendido y apagado. El nada complejo programa Java para controlar el diodo y un vídeo de su funcionamiento están a continuación.

En un artículo anterior comento como disponer de un entorno para desarrollar, desplegar las librerías jar en la Raspberry Pi y como ejecutar los ejemplos desde la línea de comandos usando una combinación de herramientas de SSH, rsync y Ansible.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew executePinBlink.

Variable not found: Enlaces interesantes 273

$
0
0
Enlaces interesantesAquí tenéis una nueva recopilación de enlaces interesantes, que recoge todo aquello que me llamó la atención durante la semana pasada, con especial atención al lanzamiento de Visual Studio 2017 :)

Por cierto, a partir de ahora voy a utilizar otro criterio de categorización, uniendo .NET y .NET Core en una única categoría, y haciendo lo mismo con ASP.NET y ASP.NET Core, porque cada vez iba teniendo menos sentido mantener esta separación.

Como siempre, espero que estos links os resulten interesantes. :-)

.NET/.NET Core

ASP.NET/ASP.NET Core

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Cross-platform

Otros


Publicado en Variable not found

Poesía Binaria: Experimento: Creando un nuevo effecto de imagen para el secuenciador de vídeo de Blender (VSE) con código fuente

$
0
0


Lo que pongo aquí es solo un experimento. Como dije en mi anterior post, me encanta Blender para hacer montajes de vídeo y para mí, es el mejor editor de vídeo disponible para GNU/Linux. Aunque tiene un pequeño defecto, bueno, muy grande… apenas tiene efectos para los vídeos, por lo que si quieres algún efecto que no sea un crossfade, una corrección de color, o un blur, vas a tener que hacerlo de otra forma y eso implica exportar vídeo y luego importarlo cuando esté hecho. Con el gran inconveniente de que si el efecto no ha quedado exactamente como quieres, si de verdad te quedan ganas para modificarlo tienes que repetir el proceso.

Me gustan los efectos, y quiero hacer pruebas, porque no había mirado mucho el código fuente de Blender, me parece una bestia, tiene muchas cosas, y partes que se han quedado un poco viejas, como es el VSE (Video Sequence Editor), el módulo de Blender que más utilizo. Así que, ¿por qué no empezar a crear algunos efectos?

Construyendo Blender

Lo malo de todo esto, es que al contrario que con programas privativos, Blender no soporta plugins para efectos. Lo hizo en el pasado, pero por razones que no llego a comprender (supongo que relacionadas con el mantenimiento, con que nadie quiso hacerse cargo del módulo y con que tal vez les reportaban muchos fallos sobre efectos cuando el fallo no estaba en Blender, sino en el que realizaba el propio efecto). Lo malo, es que si queremos incluir un nuevo efecto en Blender, debemos recompilar nuestra versión de Blender. No tarda mucho, pero tiene algunas dependencias algo pesadas. Una vez que lo has hecho, las siguientes compilaciones no tardarán mucho, ya que sólo compila el código nuevo (lo que hacemos nosotros).

Bueno, he dicho que Blender no tiene API… ¡claro que tiene! Lo malo es que es una API en Python, y personalmente (que lo he probado), efectos que renderizan a 25fps hechos en C, van a 0.3fps hechos en Python. Es cierto que no he utilizado ninguna de las fantásticas bibliotecas de imagen digital disponibles para Python, pero de todas formas hay que hacer que el vídeo se exporte a Python, y sólo lo he conseguido hacer con alguna guarrada que me da vergüenza mencionar aquí…

Lo primero es decargarte el código fuente de la página principal o del git oficial de Blender (git://git.blender.org/blender.git). Si vas a compartir lo que haces con la comunidad, te recomiendo utilizar git para poder enviar parches y demás.

El caso es que si queremos compilar Blender, necesitamos un entorno de compilación GCC, y las siguientes bibliotecas con sus cabeceras para programación: python, numpy, libx11, libjpeg, libpng, boost, ffmpeg, libopencolorio, libilm, libopenexr y algunas más. Las encuentras todas en blender/build_files/build_environment/install_deps.sh

Antes de meterme con código

Si echas un vistazo por encima al código parece muchísimo, ¡ pero no es tanto ! Como es un programa tan grande, en muchos archivos, para que sepamos qué tenemos que tocar. Como no puedo decir la línea exacta ya que este programa se modifica a menudo he decidido poner unas cuantas líneas, o funciones completas a modo de referencia. Cuando lo que nos afecta es lo referente a CUSTOM_EFFECT.

¡Empezamos con la aventura!

Botón en la interfaz

Lo primero, es que desde la interfaz podamos añadir un nuevo efecto. Que cuando estemos en el secuenciador, pulsando Shift+A -> Effect aparezca nuestro efecto en el menú, para eso, tenemos que editar

Para eso, editamos el archivo source/blender/makesdna/DNA_sequence_types.h y añadimos una nueva constante con nuestro effecto. Mi efecto se llamará CUSTOMEFFECT, además, incrementamos SEQ_TYPE_MAX. En mi caso, la cosa queda más o menos así (cerca de la línea 500, en la versión 2.78c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
enum{
    SEQ_TYPE_IMAGE       =0,
    SEQ_TYPE_META        =1,
    SEQ_TYPE_SCENE       =2,
    SEQ_TYPE_MOVIE       =3,
    SEQ_TYPE_SOUND_RAM   =4,
    SEQ_TYPE_SOUND_HD    =5,
    SEQ_TYPE_MOVIECLIP   =6,
    SEQ_TYPE_MASK        =7,

    SEQ_TYPE_EFFECT      =8,
    SEQ_TYPE_CROSS       =8,
    SEQ_TYPE_ADD         =9,
    SEQ_TYPE_SUB         =10,
    SEQ_TYPE_ALPHAOVER   =11,
    SEQ_TYPE_ALPHAUNDER  =12,
    SEQ_TYPE_GAMCROSS    =13,
    SEQ_TYPE_MUL         =14,
    SEQ_TYPE_OVERDROP    =15,
    /* SEQ_TYPE_PLUGIN      = 24, *//* Deprecated */
    SEQ_TYPE_WIPE        =25,
    SEQ_TYPE_GLOW        =26,
    SEQ_TYPE_TRANSFORM   =27,
    SEQ_TYPE_COLOR       =28,
    SEQ_TYPE_SPEED       =29,
    SEQ_TYPE_MULTICAM    =30,
    SEQ_TYPE_ADJUSTMENT  =31,
    SEQ_TYPE_GAUSSIAN_BLUR =40,
    SEQ_TYPE_TEXT =41,
    SEQ_TYPE_CUSTOMEFFECT =42,

    SEQ_TYPE_MAX  =42
};

Nuestro efecto, tendrá el índice 42, aunque no nos importa mucho la posición que ocupe. Ahora vamos al archivo source/blender/editors/space_sequencer/sequencer_edit.c, metemos el mismo efecto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
EnumPropertyItem sequencer_prop_effect_types[]={
    {SEQ_TYPE_CROSS,"CROSS",0,"Crossfade","Crossfade effect strip type"},
    {SEQ_TYPE_ADD,"ADD",0,"Add","Add effect strip type"},
    {SEQ_TYPE_SUB,"SUBTRACT",0,"Subtract","Subtract effect strip type"},
    {SEQ_TYPE_ALPHAOVER,"ALPHA_OVER",0,"Alpha Over","Alpha Over effect strip type"},
    {SEQ_TYPE_ALPHAUNDER,"ALPHA_UNDER",0,"Alpha Under","Alpha Under effect strip type"},
    {SEQ_TYPE_GAMCROSS,"GAMMA_CROSS",0,"Gamma Cross","Gamma Cross effect strip type"},
    {SEQ_TYPE_MUL,"MULTIPLY",0,"Multiply","Multiply effect strip type"},
    {SEQ_TYPE_OVERDROP,"OVER_DROP",0,"Alpha Over Drop","Alpha Over Drop effect strip type"},
    {SEQ_TYPE_WIPE,"WIPE",0,"Wipe","Wipe effect strip type"},
    {SEQ_TYPE_GLOW,"GLOW",0,"Glow","Glow effect strip type"},
    {SEQ_TYPE_TRANSFORM,"TRANSFORM",0,"Transform","Transform effect strip type"},
    {SEQ_TYPE_COLOR,"COLOR",0,"Color","Color effect strip type"},
    {SEQ_TYPE_SPEED,"SPEED",0,"Speed","Color effect strip type"},
    {SEQ_TYPE_MULTICAM,"MULTICAM",0,"Multicam Selector",""},
    {SEQ_TYPE_ADJUSTMENT,"ADJUSTMENT",0,"Adjustment Layer",""},
    {SEQ_TYPE_GAUSSIAN_BLUR,"GAUSSIAN_BLUR",0,"Gaussian Blur",""},
    {SEQ_TYPE_TEXT,"TEXT",0,"Text",""},
    {SEQ_TYPE_CUSTOMEFFECT,"CUSTOMEFFECT",0,"Custom Effect","Create a strip with customized effect"},
    {0, NULL,0, NULL, NULL}
};

Dentro de cada elemento del array tendremos:

  • Índice, para lo que cogemos un valor de nuestro enum de antes. Será de uso interno,
  • String clave, será una cadena en clave, que manejaremos desde Python para referirnos al efecto.
  • El 0 siguiente indica que no hay icono asociado, por ahora no tenemos iconos
  • Luego el nombre del efecto, para mostrarlo
  • Por último, la descripción del efecto

Ahora en el archivo source/blender/makesrna/intern/rna_sequencer_api.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop)
{
    StructRNA *srna;
    PropertyRNA *parm;
    FunctionRNA *func;

    static EnumPropertyItem seq_effect_items[]={
        {SEQ_TYPE_CROSS,"CROSS",0,"Cross",""},
        {SEQ_TYPE_ADD,"ADD",0,"Add",""},
        {SEQ_TYPE_SUB,"SUBTRACT",0,"Subtract",""},
        {SEQ_TYPE_ALPHAOVER,"ALPHA_OVER",0,"Alpha Over",""},
        {SEQ_TYPE_ALPHAUNDER,"ALPHA_UNDER",0,"Alpha Under",""},
        {SEQ_TYPE_GAMCROSS,"GAMMA_CROSS",0,"Gamma Cross",""},
        {SEQ_TYPE_MUL,"MULTIPLY",0,"Multiply",""},
        {SEQ_TYPE_OVERDROP,"OVER_DROP",0,"Over Drop",""},
        {SEQ_TYPE_WIPE,"WIPE",0,"Wipe",""},
        {SEQ_TYPE_GLOW,"GLOW",0,"Glow",""},
        {SEQ_TYPE_TRANSFORM,"TRANSFORM",0,"Transform",""},
        {SEQ_TYPE_COLOR,"COLOR",0,"Color",""},
        {SEQ_TYPE_SPEED,"SPEED",0,"Speed",""},
        {SEQ_TYPE_MULTICAM,"MULTICAM",0,"Multicam Selector",""},
        {SEQ_TYPE_ADJUSTMENT,"ADJUSTMENT",0,"Adjustment Layer",""},
        {SEQ_TYPE_GAUSSIAN_BLUR,"GAUSSIAN_BLUR",0,"Gaussian Blur",""},
        {SEQ_TYPE_TEXT,"TEXT",0,"Text",""},
        {SEQ_TYPE_CUSTOMEFFECT,"CUSTOM_EFFECT",0,"Custom Effect",""},
        {0, NULL,0, NULL, NULL}
    };

Seguimos con la interfaz de Python. Para que el elemento aparezca en el menú debemos incluir lo siguiente en el archivo release/scripts/startup/bl_ui/space_sequencer.py (añadir la última línea, más o menos alrededor de la línea 380, blender 2.78c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class SEQUENCER_MT_add_effect(Menu):
    bl_label ="Effect Strip..."

    def draw(self, context):
        layout =self.layout

        layout.operator_context='INVOKE_REGION_WIN'

        layout.operator("sequencer.effect_strip_add", text="Add").type='ADD'
        layout.operator("sequencer.effect_strip_add", text="Subtract").type='SUBTRACT'
        layout.operator("sequencer.effect_strip_add", text="Alpha Over").type='ALPHA_OVER'
        layout.operator("sequencer.effect_strip_add", text="Alpha Under").type='ALPHA_UNDER'
        layout.operator("sequencer.effect_strip_add", text="Cross").type='CROSS'
        layout.operator("sequencer.effect_strip_add", text="Gamma Cross").type='GAMMA_CROSS'
        layout.operator("sequencer.effect_strip_add", text="Gaussian Blur").type='GAUSSIAN_BLUR'
        layout.operator("sequencer.effect_strip_add", text="Multiply").type='MULTIPLY'
        layout.operator("sequencer.effect_strip_add", text="Over Drop").type='OVER_DROP'
        layout.operator("sequencer.effect_strip_add", text="Wipe").type='WIPE'
        layout.operator("sequencer.effect_strip_add", text="Glow").type='GLOW'
        layout.operator("sequencer.effect_strip_add", text="Text").type='TEXT'
        layout.operator("sequencer.effect_strip_add", text="Transform").type='TRANSFORM'
        layout.operator("sequencer.effect_strip_add", text="Color").type='COLOR'
        layout.operator("sequencer.effect_strip_add", text="Speed Control").type='SPEED'
        layout.operator("sequencer.effect_strip_add", text="Multicam Selector").type='MULTICAM'
        layout.operator("sequencer.effect_strip_add", text="Adjustment Layer").type='ADJUSTMENT'
        layout.operator("sequencer.effect_strip_add", text="Custom Effect").type='CUSTOM_EFFECT'

Ahora definimos el color del elemento una vez lo pongamos en la línea de tiempo. Para ello en source/blender/editors/space_sequencer/sequencer_draw.c vamos a la función color3ubv_from_seq() donde, dentro del case, dejamos algo como esto (es aproximado, podemos definirle el color si queremos, pero yo me he basado en el color de otros elementos parecidos):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        /* effects */
        case SEQ_TYPE_TRANSFORM:
        case SEQ_TYPE_SPEED:
        case SEQ_TYPE_ADD:
        case SEQ_TYPE_SUB:
        case SEQ_TYPE_MUL:
        case SEQ_TYPE_ALPHAOVER:
        case SEQ_TYPE_ALPHAUNDER:
        case SEQ_TYPE_OVERDROP:
        case SEQ_TYPE_GLOW:
        case SEQ_TYPE_MULTICAM:
        case SEQ_TYPE_ADJUSTMENT:
        case SEQ_TYPE_GAUSSIAN_BLUR:
        case SEQ_TYPE_CUSTOMEFFECT:
            UI_GetThemeColor3ubv(TH_SEQ_EFFECT, col);

            /* slightly offset hue to distinguish different effects */
            if     (seq->type == SEQ_TYPE_ADD)          rgb_byte_set_hue_float_offset(col,0.04);
            elseif(seq->type == SEQ_TYPE_SUB)          rgb_byte_set_hue_float_offset(col,0.08);
            elseif(seq->type == SEQ_TYPE_MUL)          rgb_byte_set_hue_float_offset(col,0.12);
            elseif(seq->type == SEQ_TYPE_ALPHAOVER)    rgb_byte_set_hue_float_offset(col,0.16);
            elseif(seq->type == SEQ_TYPE_ALPHAUNDER)   rgb_byte_set_hue_float_offset(col,0.20);
            elseif(seq->type == SEQ_TYPE_OVERDROP)     rgb_byte_set_hue_float_offset(col,0.24);
            elseif(seq->type == SEQ_TYPE_GLOW)         rgb_byte_set_hue_float_offset(col,0.28);
            elseif(seq->type == SEQ_TYPE_TRANSFORM)    rgb_byte_set_hue_float_offset(col,0.36);
            elseif(seq->type == SEQ_TYPE_MULTICAM)     rgb_byte_set_hue_float_offset(col,0.32);
            elseif(seq->type == SEQ_TYPE_ADJUSTMENT)   rgb_byte_set_hue_float_offset(col,0.40);
            elseif(seq->type == SEQ_TYPE_GAUSSIAN_BLUR) rgb_byte_set_hue_float_offset(col,0.42);
            elseif(seq->type == SEQ_TYPE_CUSTOMEFFECT) rgb_byte_set_hue_float_offset(col,0.52);
            break;

Si compilamos y ejecutamos Blender, seguramente nuestro efecto aparezca y lo podremos añadir, pero no hará nada, sólo soltar errores por la consola.

Definiendo algunas propiedades

Debemos definir antes de continuar algunas propiedades básicas del strip. En source/blender/makesrna/intern/rna_sequencer.c haremos varias modificaciones. Primero, en la función rna_def_sequence() (alrededor de la línea 1380), volvemos a meter nuestro efecto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
staticvoid rna_def_sequence(BlenderRNA *brna)
{
    StructRNA *srna;
    PropertyRNA *prop;

    staticconst EnumPropertyItem seq_type_items[]={
        {SEQ_TYPE_IMAGE,"IMAGE",0,"Image",""},
        {SEQ_TYPE_META,"META",0,"Meta",""},
        {SEQ_TYPE_SCENE,"SCENE",0,"Scene",""},
        {SEQ_TYPE_MOVIE,"MOVIE",0,"Movie",""},
        {SEQ_TYPE_MOVIECLIP,"MOVIECLIP",0,"Clip",""},
        {SEQ_TYPE_MASK,"MASK",0,"Mask",""},
        {SEQ_TYPE_SOUND_RAM,"SOUND",0,"Sound",""},
        {SEQ_TYPE_CROSS,"CROSS",0,"Cross",""},
        {SEQ_TYPE_ADD,"ADD",0,"Add",""},
        {SEQ_TYPE_SUB,"SUBTRACT",0,"Subtract",""},
        {SEQ_TYPE_ALPHAOVER,"ALPHA_OVER",0,"Alpha Over",""},
        {SEQ_TYPE_ALPHAUNDER,"ALPHA_UNDER",0,"Alpha Under",""},
        {SEQ_TYPE_GAMCROSS,"GAMMA_CROSS",0,"Gamma Cross",""},
        {SEQ_TYPE_MUL,"MULTIPLY",0,"Multiply",""},
        {SEQ_TYPE_OVERDROP,"OVER_DROP",0,"Over Drop",""},
        {SEQ_TYPE_WIPE,"WIPE",0,"Wipe",""},
        {SEQ_TYPE_GLOW,"GLOW",0,"Glow",""},
        {SEQ_TYPE_TRANSFORM,"TRANSFORM",0,"Transform",""},
        {SEQ_TYPE_COLOR,"COLOR",0,"Color",""},
        {SEQ_TYPE_SPEED,"SPEED",0,"Speed",""},
        {SEQ_TYPE_MULTICAM,"MULTICAM",0,"Multicam Selector",""},
        {SEQ_TYPE_ADJUSTMENT,"ADJUSTMENT",0,"Adjustment Layer",""},
        {SEQ_TYPE_GAUSSIAN_BLUR,"GAUSSIAN_BLUR",0,"Gaussian Blur",""},
        {SEQ_TYPE_TEXT,"TEXT",0,"Text",""},
        {SEQ_TYPE_CUSTOMEFFECT,"CUSTOM_EFFECT",0,"Custom Effect",""},             
        {0, NULL,0, NULL, NULL}
    };

Luego en la línea 2390 más o menos modificamos la estructura def_effects añadiendo la definición de nuestro nuevo efecto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static EffectInfo def_effects[]={
    {"AddSequence","Add Sequence","Add Sequence", NULL,2},
    {"AdjustmentSequence","Adjustment Layer Sequence",
     "Sequence strip to perform filter adjustments to layers below", rna_def_input,0},
    {"AlphaOverSequence","Alpha Over Sequence","Alpha Over Sequence", NULL,2},
    {"AlphaUnderSequence","Alpha Under Sequence","Alpha Under Sequence", NULL,2},
    {"ColorSequence","Color Sequence",
     "Sequence strip creating an image filled with a single color", rna_def_solid_color,0},
    {"CrossSequence","Cross Sequence","Cross Sequence", NULL,2},
    {"GammaCrossSequence","Gamma Cross Sequence","Gamma Cross Sequence", NULL,2},
    {"GlowSequence","Glow Sequence","Sequence strip creating a glow effect", rna_def_glow,1},
    {"MulticamSequence","Multicam Select Sequence","Sequence strip to perform multicam editing",
     rna_def_multicam,0},
    {"MultiplySequence","Multiply Sequence","Multiply Sequence", NULL,2},
    {"OverDropSequence","Over Drop Sequence","Over Drop Sequence", NULL,2},
    {"SpeedControlSequence","SpeedControl Sequence",
     "Sequence strip to control the speed of other strips", rna_def_speed_control,1},
    {"SubtractSequence","Subtract Sequence","Subtract Sequence", NULL,2},
    {"TransformSequence","Transform Sequence",
     "Sequence strip applying affine transformations to other strips", rna_def_transform,1},
    {"WipeSequence","Wipe Sequence","Sequence strip creating a wipe transition",
     rna_def_wipe,2},
    {"GaussianBlurSequence","Gaussian Blur Sequence","Sequence strip creating a gaussian blur",
     rna_def_gaussian_blur,1},
    {"TextSequence","Text Sequence","Sequence strip creating text",
     rna_def_text,0},
    {"CustomEffectSequence","Custom Effect Sequence","Sequence strip creating custom effects",
     rna_def_custom_effect,1},
    {"","","", NULL,0}
};

Donde, el 1 que acompaña la declaración del efecto es el número de entradas que necesitamos. Al aplicarse sobre otro strip, necesitamos un 1 aquí. Si fuera un generador, sería 0 y una transición tendría 2.

Ahora, junto con los demás efectos (línea 2100 más o menos), creamos la función rna_def_customeffect donde definiremos los parámetros que se podrán tocar de nuestro efecto, que luego tendremos que vincular con Python, que no se nos olvide:

1
2
3
4
5
6
7
8
9
10
staticvoid rna_def_custom_effect(StructRNA *srna)
{
    PropertyRNA *prop;

    RNA_def_struct_sdna_from(srna,"CustomEffectVars","effectdata");
    prop = RNA_def_property(srna,"property", PROP_FLOAT, PROP_UNSIGNED);
    RNA_def_property_ui_text(prop,"One property","One float prop for practising");
    RNA_def_property_ui_range(prop,0.0f, FLT_MAX,1,-1);
    RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER,"rna_Sequence_update");
}

Y añadimos nuestro efecto a la función rna_sequence_refine():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
static StructRNA *rna_Sequence_refine(struct PointerRNA *ptr)
{
    Sequence *seq =(Sequence *)ptr->data;

    switch(seq->type){
        case SEQ_TYPE_IMAGE:
            return&RNA_ImageSequence;
        case SEQ_TYPE_META:
            return&RNA_MetaSequence;
        case SEQ_TYPE_SCENE:
            return&RNA_SceneSequence;
        case SEQ_TYPE_MOVIE:
            return&RNA_MovieSequence;
        case SEQ_TYPE_MOVIECLIP:
            return&RNA_MovieClipSequence;
        case SEQ_TYPE_MASK:
            return&RNA_MaskSequence;
        case SEQ_TYPE_SOUND_RAM:
            return&RNA_SoundSequence;
        case SEQ_TYPE_CROSS:
            return&RNA_CrossSequence;
        case SEQ_TYPE_ADD:
            return&RNA_AddSequence;
        case SEQ_TYPE_SUB:
            return&RNA_SubtractSequence;
        case SEQ_TYPE_ALPHAOVER:
            return&RNA_AlphaOverSequence;
        case SEQ_TYPE_ALPHAUNDER:
            return&RNA_AlphaUnderSequence;
        case SEQ_TYPE_GAMCROSS:
            return&RNA_GammaCrossSequence;
        case SEQ_TYPE_MUL:
            return&RNA_MultiplySequence;
        case SEQ_TYPE_OVERDROP:
            return&RNA_OverDropSequence;
        case SEQ_TYPE_MULTICAM:
            return&RNA_MulticamSequence;
        case SEQ_TYPE_ADJUSTMENT:
            return&RNA_AdjustmentSequence;
        case SEQ_TYPE_WIPE:
            return&RNA_WipeSequence;
        case SEQ_TYPE_GLOW:
            return&RNA_GlowSequence;
        case SEQ_TYPE_TRANSFORM:
            return&RNA_TransformSequence;
        case SEQ_TYPE_COLOR:
            return&RNA_ColorSequence;
        case SEQ_TYPE_SPEED:
            return&RNA_SpeedControlSequence;
        case SEQ_TYPE_GAUSSIAN_BLUR:
            return&RNA_GaussianBlurSequence;
        case SEQ_TYPE_TEXT:
            return&RNA_TextSequence;
        case SEQ_TYPE_CUSTOM_EFFECT:
            return&RNA_CustomEffectSequence;
        default:
            return&RNA_Sequence;
    }
}

Por último en source/blender/makesrna/RNA_access.h metemos esta línea, más o menos en la 280:

1
extern StructRNA RNA_CustomEffectSequence;

Insertamos las propiedades de nuestro efecto

Por ahora sólo estoy metiendo una, y no hace nada, sólo estar ahí, pero me sirve como plantilla para futuros efectos. Antes de que se nos olvide, vamos a añadirla desde la interfaz de Python. En el archivo en el archivo release/scripts/startup/bl_ui/space_sequencer.py en la clase CLASS_PT_EFFECT(), al final del todo añadimos nuestro efecto:

1
2
3
4
5
6
7
8
9
10
11
12
         col = layout.column(align=True)
        if strip.type=='SPEED':
            col.prop(strip,"multiply_speed")
        elif strip.typein{'CROSS','GAMMA_CROSS','WIPE','ALPHA_OVER','ALPHA_UNDER','OVER_DROP'}:
            col.prop(strip,"use_default_fade","Default fade")
            ifnot strip.use_default_fade:
                col.prop(strip,"effect_fader", text="Effect fader")
        elif strip.type=='GAUSSIAN_BLUR':
            col.prop(strip,"size_x")
            col.prop(strip,"size_y")
        elif strip.type=='CUSTOM_EFFECT':
            col.prop(strip,'property')

Ahora declaramos las propiedades de nuestro efecto. Para ello en el archivo source/blender/makesdna/DNA_sequence_types.h insertamos lo sigueinte (cerca de la 290, junto con los demás efectos, ya los veréis):

1
2
3
typedefstruct CustomEffectVars {
    float property;
} CustomEffectVars;

Mi propiedad se llamaba property, porque soy muy original, pero aquí pondremos las que necesitemos. Podremos poner arrays de char para cadenas de caracteres, char, short para flags, enteros y float, por ejemplo, los colores van en float. Podríamos poner otros tipos de variable, pero no he probado… no soy tan valiente.

Para que las propiedades de nuestro efecto se guarden cuando salvamos el archivo .blend hacemos, editamos source/blender/blenloader/intern/writefile.c añadiendo la estructura de nuestro efecto, alrededor de la límea 2660:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
            SEQ_BEGIN(ed, seq)
            {
                if(seq->strip && seq->strip->done ==0){
                    /* write strip with 'done' at 0 because readfile */

                    if(seq->effectdata){
                        switch(seq->type){
                            case SEQ_TYPE_COLOR:
                                writestruct(wd, DATA, SolidColorVars,1, seq->effectdata);
                                break;
                            case SEQ_TYPE_SPEED:
                                writestruct(wd, DATA, SpeedControlVars,1, seq->effectdata);
                                break;
                            case SEQ_TYPE_WIPE:
                                writestruct(wd, DATA, WipeVars,1, seq->effectdata);
                                break;
                            case SEQ_TYPE_GLOW:
                                writestruct(wd, DATA, GlowVars,1, seq->effectdata);
                                break;
                            case SEQ_TYPE_TRANSFORM:
                                writestruct(wd, DATA, TransformVars,1, seq->effectdata);
                                break;
                            case SEQ_TYPE_GAUSSIAN_BLUR:
                                writestruct(wd, DATA, GaussianBlurVars,1, seq->effectdata);
                                break;
                            case SEQ_TYPE_CUSTOMEFFECT:
                                writestruct(wd, DATA, CustomEffectVars,1, seq->effectdata);
                                break;
                            case SEQ_TYPE_TEXT:
                                writestruct(wd, DATA, TextVars,1, seq->effectdata);
                                break;
                        }
                    }

                    writestruct(wd, DATA, Stereo3dFormat,1, seq->stereo3d_format);

Definiciones de nuestro efecto

Ya va quedando menos, tenemos esto casi configurado, sólo quedan algunos callbacks con las llamadas que hacen los propios filtros, vamos, ahora es cuando nuestro filtro hace algo, aunque es necesario definirlo para que el programa no explote:

En el archivo source/blender/blenkernel/intern/seqeffects.c encontramos el código de estos efectos. Lo primero será introducir las definiciones de nuestro efecto en la función get_sequence_effect_impl() en la línea 3315 o así, buscamos los efectos del switch y ponemos el nuestro al final:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        case SEQ_TYPE_ADJUSTMENT:
            rval.supports_mask=true;
            rval.num_inputs= num_inputs_adjustment;
            rval.early_out= early_out_adjustment;
            rval.execute= do_adjustment;
            break;
        case SEQ_TYPE_GAUSSIAN_BLUR:
            rval.init= init_gaussian_blur_effect;
            rval.num_inputs= num_inputs_gaussian_blur;
            rval.free= free_gaussian_blur_effect;
            rval.copy= copy_gaussian_blur_effect;
            rval.early_out= early_out_gaussian_blur;
            rval.execute= do_gaussian_blur_effect;
            break;
        case SEQ_TYPE_TEXT:
            rval.num_inputs= num_inputs_text;
            rval.init= init_text_effect;
            rval.free= free_effect_default;
            rval.copy= copy_effect_default;
            rval.early_out= early_out_text;
            rval.execute= do_text_effect;
            break;
        case SEQ_TYPE_CUSTOMEFFECT:
            rval.init= init_custom_effect_effect;
            rval.num_inputs= num_inputs_custom_effect;
            rval.free= free_custom_effect_effect;
            rval.copy= copy_custom_effect_effect;
            rval.early_out= early_out_custom_effect;
            rval.execute= do_custom_effect_effect;
            break;

Aquí definiremos:

  • rval.init : función que inicializa nuestro efecto
  • rval.num_inputs : número de strips de entrada que necesita nuestro strip. Los efectos necesitan estas sobre un strip, las transiciones sobre dos, los generadores no necesitan strips.
  • rval.free : Qué hacemos cuando se quita nuestro efecto (para liberar memoria)
  • rval.copy : Qué hacer cuando copian nuestro efecto.
  • rval.early_out : A veces, podemos saber si es necesario o no aplicar el efecto antes de ponernos a procesar. Aquí veremos si procesamos, si devolvemos el valor de la entrada, o no devolvemos nada
  • rval.execute : NUESTRO EFECTO !!!
  • rval.supports_mask : ¿Soporta máscaras? (es un booleano, no un callback
  • rval.load : Qué hacer cuando se acaba de cargar el efecto?
  • rval.init_execution : Este callback se ejecutará antes de hacer el render de nuestro efecto.
  • rva.multithreaded : El efecto soporta threads. Y en lugar de llamar a rval.execute se llamará a rval.execute_slice, por lo que si hacemos nuestro efecto multihilo tendremos que hacerlo independiente de las líneas de origen y fin que nos pasen (Blender ya se encarga de crear hilos y de todo lo demás, nosotros sólo tenemos que definir lo que hace cada uno).

Encontraremos toda la información en source/blender/blenkernel/BKE_sequencer.h

Como todo lo que hemos puesto son callbacks, tendremos que definirlos, voy a poner todo el código seguido, pero si queréis tenerlo ordenado buscad funciones parecidas de otros efectos y poned el vuestro por ahí:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*********************** Custom Effect *************************/
staticvoid init_custom_effect_effect(Sequence *seq)
{
    if(seq->effectdata)
        MEM_freeN(seq->effectdata);

    seq->effectdata = MEM_callocN(sizeof(CustomEffectVars),"customeffectvars");
}

staticint num_inputs_custom_effect(void)
{
    return1;
}

staticvoid free_custom_effect_effect(Sequence *seq)
{
    if(seq->effectdata)
        MEM_freeN(seq->effectdata);

    seq->effectdata = NULL;
}

staticvoid copy_custom_effect_effect(Sequence *dst, Sequence *src)
{
    dst->effectdata = MEM_dupallocN(src->effectdata);
}

staticint early_out_custom_effect(Sequence *seq,float UNUSED(facf0),float UNUSED(facf1))
{

    /* source/blender/blenkernel/BKE_sequencer.h */
    /* #define EARLY_NO_INPUT      -1 */
    /* #define EARLY_DO_EFFECT     0 */
    /* #define EARLY_USE_INPUT_1   1 */
    /* #define EARLY_USE_INPUT_2   2 */
   
    CustomEffectVars *UNUSED(data)= seq->effectdata;
    /* I will always do my effect */
    /* if (data->property == 0) { */
    /*  return EARLY_USE_INPUT_1; */
    /* } */
    return EARLY_DO_EFFECT;
}

// NUESTRO EFECTO !!!!!!!!!!!!!
static ImBuf *do_custom_effect_effect(const SeqRenderData *context, Sequence *seq,float UNUSED(cfra),float UNUSED(facf0),float UNUSED(facf1),
                                     ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
    ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);

    CustomEffectVars *cv =(CustomEffectVars *)seq->effectdata;

    unsignedchar*rect,*orig;
    int x= context->rectx;
    int y= context->recty;
    float prop = cv->property;

    if(out->rect){
        orig =(unsignedchar*)ibuf1->rect;
        rect =(unsignedchar*)out->rect;
        while(y--){
            x = context->rectx;
            while(x--){
                rect[0]=(orig[0]+ prop*orig[1])/(1+prop);
                rect[1]=(orig[1]+ prop*orig[2])/(1+prop);
                rect[2]=(orig[2]+ prop*orig[1])/(1+prop);
                rect[3]=220;
                rect +=4;
                orig +=4;
            }
        }
    }
    elseif(out->rect_float){
        orig =(unsignedchar*)ibuf1->rect_float;
        rect =(unsignedchar*)out->rect_float;
        while(y--){
            x = context->rectx;
            while(x--){
                rect[0]=(orig[0]+ prop*orig[1])/(1+prop);
                rect[1]=(orig[1]+ prop*orig[2])/(1+prop);
                rect[2]=(orig[2]+ prop*orig[1])/(1+prop);
                rect[3]=0.5;
                rect +=4;
                orig +=4;
               
            }
        }
    }
    return out;
}

¡Y listo! Si compilamos Blender ya tenemos nuestro efecto.

En la función do_custom_effect_effect(), tenemos los siguientes parámetros para la entrada:

  • const SeqRenderData *context : El contexto, aquí tenemos acceso a muchísimas cosas, demasiado para enumerarlo aquí, pero accedemos a la estructura Main que contiene casi todo, podemos acceder a la escena, elementos guardados y miles de cosas más.
  • Sequence *seq : Toda la información del strip actual
  • float cfra: Fotograma actual, puede tener un error de +-0.5 frames.
  • float facf0: Cuando estamos operando con contenido entrelazado y estamos animando un efecto, qué porcentaje de efecto recae sobre el campo 0?
  • float facf1: lo mismo pero para el campo 1.
  • ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3: Son las imágenes asociadas al efecto, dependiendo del efecto, habrá varias imágenes implicadas en él.

Y… como extra, si no estamos utilizando una variable, pero la queremos dejar ahí, para que el compilador no se queje podemos envolverla con la macro UNUSED()

La comunidad Blender

En https://developer.blender.org/ encontramos un sistema en el que se ponen en común bugs y pequeñas características (si quieres colaborar en algo grande, tienes que contactar con ellos). Y actualmente el secuenciador lo tienen un poco abandonado, de todas formas, hay una parte importante de la comunidad que sigue ahí, enviando parches y mejoras.

The post Experimento: Creando un nuevo effecto de imagen para el secuenciador de vídeo de Blender (VSE) con código fuente appeared first on Poesía Binaria.

Variable not found: ¿Quién libera las dependencias cuando finaliza el proceso de una petición en ASP.NET Core?

$
0
0
ASP.NET CoreEl sistema de inyección de dependencias de ASP.NET Core es un potente mecanismo que viene de serie en el framework y, que de hecho, es uno de los principales pilares en los que se basa su funcionamiento.

Probablemente sabréis (y si no, podéis echar un vistazo a este post) que existen diversas fórmulas para añadir servicios o componentes al contenedor de dependencias, y una de ellas es la denominada "Scoped". Estos componentes quedan registrados asociados a un ciclo de vida específico (ServiceLifetime.Scoped), que indica que cada vez que haya que se solicite un objeto de este tipo se retornará la misma instancia dentro el ámbito actual, que normalmente coincide con el proceso de una petición específica.

O en otras palabras, dentro de la misma petición, la instancia del componente será compartida por todos los objetos que dependan de él. Además, al finalizar el proceso de la petición, el framework invocará su método Dispose() (si existe, claro), permitiendo liberar los recursos que haya utilizado. Por ejemplo, suele ser una buena práctica emplear esta técnica para reutilizar un contexto de datos entre los servicios que participan en el proceso de una petición que accede a una base de datos, y asegurar su liberación final para no dejar conexiones abiertas.

Pero este post no va de ver cómo registrar estos componentes o de cómo utilizarlos en nuestras aplicaciones, sino de responder una simple curiosidad: ¿quién se encarga de crear este scope cuando comienza una petición y de liberarlo cuando finaliza? Ya, sé que no tiene mucha utilidad práctica en el día a día, pero es una buena excusa para echar un vistazo y entender cómo funcionan las cosas por dentro ;)

<Disclaimer>Todo lo que comentaremos a continuación son detalles de la implementación actual del framework y son válidos en la versión 1.1 de ASP.NET Core, pero podrían cambiar en futuras versiones.</Disclaimer>

Como sabéis, el método Configure() de la clase de inicialización de ASP.NET Core recibe una instancia de IApplicationBuilder que representa el pipeline de nuestra aplicación. Durante el proceso de configuración, utilizaremos esta instancia para añadir los middlewares que necesitemos en nuestra aplicación.

Pero si alguna vez habéis introducido un breakpoint en este método y habéis analizado el contenido que recibimos aquí, veréis que enmascarado tras el interfaz IApplicationBuilder se encuentra un objeto ApplicationBuilder que, a diferencia de lo que podríamos esperar, viene con el pipeline ya poblado con algunos middlewares, como se muestra en la siguiente captura de pantalla:

Instancia de IApplicationBuilder recibida en el método Configure()
De los tres middlewares incluidos por defecto, los dos últimos tienen que ver con la integración con IIS, pero el que nos interesa especialmente es el primero de ellos, que si escarbamos un poco podemos ver que se trata de un objeto RequestServicesContainerMiddleware.

Este middleware, definido de forma interna en el paquete Microsoft.AspNetCore.Hosting, está posicionado justo a la entrada del pipeline y básicamente se encarga de crear un scope justo cuando llega una petición, ceder el control al resto del pipeline y, finalmente, liberarlo junto con los objetos asociados al mismo cuando vuelve a retomar el control, es decir, cuando el proceso de la petición ha finalizado completamente:
public async Task Invoke(HttpContext httpContext)
{
[...]
using (var feature = new RequestServicesFeature(_scopeFactory))
{
try
{
httpContext.Features.Set<IServiceProvidersFeature>(feature);
await _next.Invoke(httpContext);
}
finally
{
[...]
}
}
}
La clase RequestServicesFeature que se crea en el código anterior es la que realmente implementa la instanciación del scope, y es introducida en las features disponibles en el contexto HTTP como IServiceProvidersFeature para que esté disponible durante el proceso de la petición para actuar como service provider. Es decir, todas las instancias  creadas como ServiceLifetime.Scoped serán asociadas a este proveedor de servicios.

Cuando la petición ha sido ya procesada por el resto del pipeline y el control retorna al código de este middleware, la finalización del bloque using hace que se libere el objeto RequestServicesFeature y con él los objetos scoped que hayan sido creados en su ámbito. Sencillo, ¿verdad?

En conclusión, la respuesta a la pregunta que nos hacíamos al principio es bastante simple, e incluso previsible. La creación y liberación del scope que marca la vida de las dependencias durante el proceso de una petición se realiza desde un middleware ubicado justo en la puerta del pipeline. Esta posición privilegiada le permite crear un service provider específico para la petición, que actuará como scope, y liberarlo cuando el proceso de ésta haya finalizado, junto con todos los objetos que hayan sido creados en el mismo.

Publicado en Variable not found.

Juanjo Navarro: Montando tu servidor en un VPS con Docker: Introducción

$
0
0

Últimamente he estado montando mis páginas web en un servidor VPS.

Mi intención es dedicar una serie de artículos a esto en las próximas semanas, pero antes quiero dejar escritas las motivaciones para hacerlo y algunas ideas previas.

¿Por qué montar un VPS?

  1. Me gusta “jugar” con servidores. Esta es la verdadera razón. He tenido mis páginas en un hosting compartido desde hace mucho, y es obvio que no tener que ocuparte tú del mantenimiento de los servidores es una ventaja, pero varias veces había tenido la tentación de montar un VPS y al final me he decidido.
  2. He encontrado un VPS de muy buen precio. En ocasiones he arrancado un VPS en DigitalOcean durante horas o días, para hacer alguna prueba, pero el precio que quería pagar sólo me permitía acceder al Droplet más pequeño, y me daba la sensación de que con sólo 512Mb de RAM se podía quedar corto. Ahora he encontrado otro que por el mismo precio o menos me proporciona 2Gb de RAM (más sobre esto próximamente)
  3. Me permite acceder a funcionalidades extra. Por ejemplo: Let’s Encrypt. Con la web moviéndose hacía https (y con un servicio como Let’s Encrypt, que nos ofrece certificados gratuitamente) quería sumarme a esta tendencia, pasando mis webs a https. Algunos proveedores de hosting compartido ya ofrecen soporte para Let’s Encrypt, pero el que yo usaba no lo hacía. Puestos a cambiar de hosting compartido, he decidido el do it yourself de un VPS.

¿Por qué utilizar Docker?

  1. Por probar una nueva tecnología. Nuevamente, no hay que buscar más razones que las ganas de “jugar” con una nueva tecnología. He leído sobre Docker, he hecho algunas pruebas, pero no hay nada como tener que pelearte de verdad con una tecnología para aprender sobre ella.
  2. Quiero poder cambiar rápidamente de VPS. Es obvio que el VPSbarato me puede salir rana. Aunque he hecho unas cuantas pruebas y todo parece funcionar bien, quiero tener un plan B, poder pasarme a otro VPS rápidamente en el caso de que el rendimiento no sea el esperado. Montando todo con Docker (servidores de BBDD, servidores web, etc) puedo simplemente desplegar los contenedores en otro VPS y automáticamente tener todo funcionando sin más que apuntar los DNS a la nueva IP.

Navegapolis: Arreglando bugs en producción


Koalite: Resumen 2017

$
0
0

Llegamos a final de año, época de tradiciones, y fiel a las mías toca escribir un post con el resumen de lo que ha sido el 2017 para el blog. Un post sazonado con algo de porno de estadísticas y un refrito de posts destacados, pero sin mucha más intención que despedir el año.

Estadísticas

Para mi lo más importante de escribir en el blog es aprender y divertirme. Desde esa perspectiva, los números no son demasiado importantes, pero siempre es entretenido echarles un vistazo.

Este ha sido el año que menos posts he escrito, sólo 23 posts. Eso me deja a 1 del objetivo que me había marcado, en el que ya daba por hecho que este iba a ser un año complicado. A cambio he aumentado mi actividad fuera del blog, con target=”_blank”>una charla sobre testing, un webminar sobre ClojureScript y la asistencia a las dos ediciones de DeveloperOS.

Mi objetivo para el año que viene vuelve a ser escribir 24 posts. Considerando que desde el 6 de marzo de este año tengo otras cosas más entretenidas en mi vida, me conformo con esto.

Según Google Analytics, han sido 175.000 visitantes en unas 230.000 sesiones con 300.000 páginas vistas. Comparado con el año pasado, es un descenso de entre el 12% y el 20%, según la métrica que tomes.

En este descenso influye claramente haber escrito menos posts, pero también se sigue viendo una tendencia que empezó hace unos años según iba dejando de escribir de temas de moda y hacia cosas más genéricas. Eso hace que los posts tengan muy buena acogida en twitter y me den para conversar y aprender, pero que luego no sean muy visitados desde Google. No creo que eso cambie a corto plazo, porque me resulta mucho más satisfactoria y útil una buena conversación, que un post con miles de visitas que vienen buscando un fragmento de código para copiarlo sin entenderlo.

Lo más visitado

El top 5 del año es:

  1. Tratar con Fechas en JSON en Javascript y TypeScript.
  2. Evitar la deuda técnica es un error.
  3. No pierdas el tiempo escribiendo tests.
  4. Cómo testear el frontend de una aplicación web.
  5. De monos, melanesios y desarrollo de software.

Excepto el primero, que es un post más práctico y aburrido, el resto coincide bastante con mis preferencias personales. Disfruté especialmente escribiendo sobre monos y melanesios. De los que se quedan fuera, me gustó explicar cómo crear un diccionario persistente en 4 líneas de javascript y creo que fue importante hablar de los problemas de ignorar la persistencia.

Hasta el año que viene

Con esto doy por cerrado el año en el blog. Ahora toca descansar desconectar un poco, maltratar el organismo con comidas copiosas y “disfrutar” de todos los eventos navideños.

¡Feliz 2018 a todos!

Posts relacionados:

  1. Resumen 2016
  2. Resumen 2013
  3. Resumen 2014

Variable not found: Enlaces interesantes 306

$
0
0
Enlaces interesantes
Para celebrar las fiestas, ahí va última colección de enlaces del año. Como de costumbre, espero que os resulten interesantes. :-)

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

EF

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

Jesús Perales: Modificar la interfaz de red de Docker docker0

$
0
0
Modificar la interfaz de red de Docker docker0

Normalmente Docker utiliza una serie de configuraciones internas de red, entre ellas crea una interfaz de red llamada docker0 la cual nunca nos molestara, a menos que utilices direcciones IP que comiencen con 172.x.x.x, es entonces cuando debemos crear/modificar el archivo daemon.json y agregar un segmento de red que no nos moleste.

{"bip": "192.168.200.0/16"
}

El archivo daemon.json normalmente se debe crear/encontrar en la ruta /etc/docker/daemon.json de nuestro SO.

Si usas windows lo puedes encontrar en C:\ProgramData\Docker\config\daemon.json.

Adrianistán: Gana dos entradas dobles para “Bugs” en el Museo Reina Sofía

$
0
0

En enero de 2018, el Museo Reina Sofía de Madrid acogerá la exposición Bugs, una exposición dedicada a esas obras de arte que muchas veces pasan inadvertidas, los bugs.

En esta exposición, que se expondrá hasta el 1 de abril de 2018, podremos ver algunos de los máximos exponentes de este estilo artístico surgido a finales del siglo XX y muy popular en el siglo XXI. Como yo mismo he formado parte del comité de selección de obras, tengo el honor de regalar dos entradas dobles para la exposición.

«Queremos reunir en un mismo edificio todas las sensaciones que han provocado a los usuarios. Rabia, indignación, ira y soledad.» dice Sara Echeverría, directora de exposiciones temporales del Reina Sofía y buena amiga.

Algunas de las obras, más destacadas son:

Pantalla azul de Windows sobre monitor Samsung
Pantalla azul de Windows sobre monitor LG y cables desordenados

 

Pantalla azul de Windows rotada PI/2 radianes en aeropuerto

«Las pantallas azules nos transmiten finalidad. Nos hacen ver que todo es efímero. Todo nuestro trabajo, nuestra huella en la humanidad, puede desvanecerse con un simple error de driver. Al final, no somos nada. Una gota más, del océano inmenso.» dice Bill Gates, que fue invitado para preparar la exposición. «Creemos que Bill Gates es uno de los maestros en el arte del bug, su ayuda nos ha sido indispensable para encontrar el enfoque filosófico que buscábamos».

Final de un programa en LISP

«A mí siempre me han atraído los paréntesis de LISP. Era como algo sexual. Sentía orgasmos al descubrir que podía meter más paréntesis todavía.» Y es que en la exposición también se verán obras proclives a bugs como los paréntesis de LISP o la sagrada trinidad de JavaScript.

«JavaScript iba a llenar bastantes salas, así que solamente hemos elegido lo mejor. Ha sido difícil, porque cada día salen nuevos e impresionantes bugs, en cada nueva librería o framework. Habrá que estar atentos a ellos, porque son el futuro de los bugs»

También se ha dejado una sala especial a los ciberataques, que este año han sabido aprovechar con mucha intelegencia bugs de los sistemas operativos más conocidos. ¿Cómo olvidar el WannaCry de Edvard Munch?

WannaCry, de Edvard Munch

Pero no sería  una exposición completa sin contar con bugs como el efecto 2000 o el lanzamiento del cohete Ariane 5 (esas cosas pasan por usar ints de 16 bits en un cohete).

Si queréis participar para ganar una de las dos entradas dobles para ver la exposición, solo tenéis que hacer click aquí.

La entrada Gana dos entradas dobles para “Bugs” en el Museo Reina Sofía aparece primero en Blog - Adrianistan.eu.

Blog Bitix: Películas sobre tecnología o informática, series, documentales, vídeos, libros, GNU/Linux y software libre

$
0
0

Si estás interesado en la tecnología, informática en general y en el software libre, código abierto y GNU/Linux estoy más que seguro que las siguientes listas de películas, series, documentales y libros te gustarán bastante. Probablemente algunas ya hayas visto pero otra quizá no conocías o aún no has visto y este artículo te anima a ver. En el momento de escribir esto aún no he visto todo el contenido de este artículo pero con esta lista lo tengo anotado y pendiente de ver.

Películas

Series

Documentales

En YouTube hay cantidad de documentales más.

Documentales sobre el software libre y código abierto

Probablemente ya conozcas el software libre, o quizá no, en cualquier caso te recomiendo que veas una de las conferencias de Stallman da en numerosos lugares y en las que da a conocer el software libre y promueve su uso. Cuales son los motivos éticos, morales, de seguridad y prácticos por los que preferir software libre en vez de software privativo. Si te convence y usas otros sistemas operativos como Windows o MacOS puedes empezar a conocer las distribuciones de GNU/Linux y elegir una distribución GNU/Linux de entre las varias que hay disponibles.

GNULinux

Libros y novelas

  • El circulo de Dave Eggers
  • Microsiervos de Douglas Coupland
  • Cuando los administradores de sistemas gobernaron la tierra de Cory Doctorow
  • Criptonomicon de Neal Stephenson
  • Snow Crash de Neal Stephenson
  • Ready Player One de Ernest Cline
  • ¿Sueñan los androides con ovejas eléctricas? de Philip K. Dick
  • Algunas novelas sobre informática y hackers

¿Conoces alguno imprescindible más que incluir en esta lista?

Viewing all 2734 articles
Browse latest View live