El sabor de la decepción.
Hola a todos, el día de hoy voy a hacer una pausa a la revisión del código de VEncoder, que por cierto, quiero agradecerles por la aceptación de los post. Quiero contarles algo que recientemente me paso (de hecho fue la gran decepción para un gran numero de personas, no solo para mi).
Usualmente nunca suelo entusiasmarme mucho con nada, la mayoría de las cosas que los otros encuentran divertidas, a mi me resultan aburridas, un ejemplo muy claro es el fútbol, en mi país la mayor parte de la gente es fanática de ese deporte, yo nunca entendí porque el hecho de ver triunfar a alguien más, podía darnos una gran satisfacción.
La explicación es realmente simple y tiene que ver con cuestiones psicológicas-sociales, pero para no ponerme muy profundo, la explicación breve es la siguiente: el hecho de ver a alguien más siendo capaz de hacer algo que nosotros pudiéramos considerar imposible, resulta satisfactorio, de alguna forma nos da esa sensación de "yo puedo hacerlo". Pero la cosa no solo aplica para los deportes; yo siempre me pregunte ¿cómo le hizo Einstein?, ¿cómo le hizo Sir Isaac Newton? Eso me llevo a estudiarlos un poco más y tras horas y horas de leer, llegué a la simple conclusión de que yo quería ser como ellos, al menos siquiera aspirar a lograr algo como lo que ellos hicieron, después de todo; si ellos lo lograron, ¿por que yo no?.
Pero bueno, retomando lo original, nunca me sentí entusiasmado por nada de lo "común", pero hace poco (algunos años en realidad), surgió algo que nunca me esperé, Yo iba en la vida haciendo lo que siempre hacia sin darme cuenta de que ese "algo" de forma sutil, se empezaba a arraigar cada vez más. El tiempo siguió pasando y pasando, cada vez empecé a notar que me preguntaba: ¿que pasaría si...?
Cada vez más pensaba en aquello de lo que hace poco era ajeno a mi. Una vez que me di cuenta de que ya estaba tan "contaminado" de eso, había llegado el momento de actuar, ¿que debía de hacer?, no era algo que aportara o restara cosas útiles a mi vida, tenerlo o no, no resultaba tan importante en ese momento.
No le di tanta importancia, si no lo pensaba tanto, la respuesta mas obvia, seria la que escogería, de forma inconsciente. Y así fue cómo sucedió, de forma tan progresiva y lenta, sin darme cuenta, empece a estar al tanto de "eso". Tanto fue lo que penetro en mi mente , que sin darme cuenta ya estaba especulando sobre lo que podría pasar, inventando escenarios y creando mundos ideales en los que todo esto podía acabar.
Pero no todo en la vida es perfecto, empezaron a ocurrir situaciones que resultaban un tanto molestas. La primera de ellas ocurrió hace un par de años, con antelación mencionaron un evento que marcaría un antes y después en todos los eventos relacionados, y cómo ocurre con toda primera vez, trate de no entusiasmarme demasiado, lo tome con calma. Todo ocurrió de forma un tanto confusa, a pesar de que lo que pasó no era lo que esperaba, o al menos no lo que yo mismo hube imaginado, lo deje pasar, después de todo resultaba en algo nuevo, fuera de lo normal para los estándares que siempre manejaba.
Casi un año después ocurrió algo similar, pero en esa ocasión ya estaba preparado para lo de siempre. Ocurrió lo mismo de la vez anterior, mas en esa ocasión, era lo que esperaba, lo que imagine en su momento.
Siempre he dicho que nunca hay que asumir, el resultado de un suceso aleatorio siempre es.... Aleatorio, siempre estoy consiente de eso, pero siempre hay alguien que se encargara de hacerte romper tus propios esquemas.
Empecé a notar que siempre esperaba lo mejor, todo entró en una etapa de altibajos, llegó una época de incertidumbre, pero el hecho de asumir, y que las cosas que se asumen ocurran, nunca es bueno. Ahora imagina, hay una tómbola con pelotas negras, rojas, verdes y azules, revuelves un poco la tómbola y en la primera ronda sale una bola roja, luego en la segunda ronda, también sale una roja, y así se mantiene hasta la cuarta ronda, lo más obvio que una persona haría, sería escoger la bola roja, y así pasa, en la cuarta ronda sale una bola roja y en la quinta, sexta, séptima; ya envalentonado, decides apostar todo en la ronda 8 y ganas, decides no retirarte y en la ronda 9 pones todo lo que tienes e incluso parte de lo que no. El resultado, perdiste todo porque asumiste que tu habías encontrado un patrón, cuando lo descubriste, pensaste que se mantendría y no fue así, creíste que te robaron, pero en realidad solo descubriste de forma amarga que lo único seguro en las cosas aleatorias es precisamente eso: todo es aleatorio. Y que pasa cuando hay alguien detrás de todo, un nombre y un rostro al cual culpar cuando todo sale mal, bueno eso ya depende de cada uno.
El ejemplo que mostré antes se adapta muy bien a lo que pasó, hubo tantos indicios que indicaban que el resultado que yo quera iba a pasar, que sin darme cuenta ya asumía que el resultado era auto-evidente. Yo que tantas veces le dije a los demás que nunca deberían de asumir, terminé por engañarme a mi mismo, imaginando cosas que yo quería que pasaran sin tomar en cuenta todo el contexto.
Aun confiado de lo que yo creía, espere una vez más a que las cosas pasaran, y finalmente pasó. No podía creer el resultado, todo era exactamente lo contrario a lo que imaginé, estaba profundamente decepcionado, era la primera vez en que algo me resultó tan extraño, ajeno a mi, a pesar de todo, en esencia era lo mismo, siempre lo fue, de hecho las cosas que yo imaginé en su momento, en realidad eran las cosas extrañas, que si hubieran pasado, simplemente le hubieran quitado lo que originalmente es.
Y bien , el resultado ya lo asimilé un poco y creo que fue lo mejor, aun queda un poco de esperanza, pero prefiero hacer lo que siempre hago: esperar a que ocurra lo de siempre y ya si pasa algo extraordinario, siempre será bienvenido. Ademas, a la mala logre entender a toda la "fanaticada" (a lo que sea)
Para no ponerme en evidencia, solo diré lo siguiente: El 18 de agosto fue un antes y un después.
Por ahora es todo, si lograste llegar hasta aquí, gracias por leer, significa un poco para mi el saber que a alguien más este ahí leyendo lo que pienso.
En el siguiente Post continuaremos con el código de VEncoder 2 y muy probablemente en unos días, invite a alguien más para que haga publicaciones en el blog.
Los leo luego.
Usualmente nunca suelo entusiasmarme mucho con nada, la mayoría de las cosas que los otros encuentran divertidas, a mi me resultan aburridas, un ejemplo muy claro es el fútbol, en mi país la mayor parte de la gente es fanática de ese deporte, yo nunca entendí porque el hecho de ver triunfar a alguien más, podía darnos una gran satisfacción.
La explicación es realmente simple y tiene que ver con cuestiones psicológicas-sociales, pero para no ponerme muy profundo, la explicación breve es la siguiente: el hecho de ver a alguien más siendo capaz de hacer algo que nosotros pudiéramos considerar imposible, resulta satisfactorio, de alguna forma nos da esa sensación de "yo puedo hacerlo". Pero la cosa no solo aplica para los deportes; yo siempre me pregunte ¿cómo le hizo Einstein?, ¿cómo le hizo Sir Isaac Newton? Eso me llevo a estudiarlos un poco más y tras horas y horas de leer, llegué a la simple conclusión de que yo quería ser como ellos, al menos siquiera aspirar a lograr algo como lo que ellos hicieron, después de todo; si ellos lo lograron, ¿por que yo no?.
Pero bueno, retomando lo original, nunca me sentí entusiasmado por nada de lo "común", pero hace poco (algunos años en realidad), surgió algo que nunca me esperé, Yo iba en la vida haciendo lo que siempre hacia sin darme cuenta de que ese "algo" de forma sutil, se empezaba a arraigar cada vez más. El tiempo siguió pasando y pasando, cada vez empecé a notar que me preguntaba: ¿que pasaría si...?
Cada vez más pensaba en aquello de lo que hace poco era ajeno a mi. Una vez que me di cuenta de que ya estaba tan "contaminado" de eso, había llegado el momento de actuar, ¿que debía de hacer?, no era algo que aportara o restara cosas útiles a mi vida, tenerlo o no, no resultaba tan importante en ese momento.
No le di tanta importancia, si no lo pensaba tanto, la respuesta mas obvia, seria la que escogería, de forma inconsciente. Y así fue cómo sucedió, de forma tan progresiva y lenta, sin darme cuenta, empece a estar al tanto de "eso". Tanto fue lo que penetro en mi mente , que sin darme cuenta ya estaba especulando sobre lo que podría pasar, inventando escenarios y creando mundos ideales en los que todo esto podía acabar.
Pero no todo en la vida es perfecto, empezaron a ocurrir situaciones que resultaban un tanto molestas. La primera de ellas ocurrió hace un par de años, con antelación mencionaron un evento que marcaría un antes y después en todos los eventos relacionados, y cómo ocurre con toda primera vez, trate de no entusiasmarme demasiado, lo tome con calma. Todo ocurrió de forma un tanto confusa, a pesar de que lo que pasó no era lo que esperaba, o al menos no lo que yo mismo hube imaginado, lo deje pasar, después de todo resultaba en algo nuevo, fuera de lo normal para los estándares que siempre manejaba.
Casi un año después ocurrió algo similar, pero en esa ocasión ya estaba preparado para lo de siempre. Ocurrió lo mismo de la vez anterior, mas en esa ocasión, era lo que esperaba, lo que imagine en su momento.
Siempre he dicho que nunca hay que asumir, el resultado de un suceso aleatorio siempre es.... Aleatorio, siempre estoy consiente de eso, pero siempre hay alguien que se encargara de hacerte romper tus propios esquemas.
-Me sometí tan gradualmente que no me di cuenta que me estaban manipulando.— Hugo G (@Mpm88G) 3 de julio de 2016
-La verdad siempre es reveladora#GetReady
Empecé a notar que siempre esperaba lo mejor, todo entró en una etapa de altibajos, llegó una época de incertidumbre, pero el hecho de asumir, y que las cosas que se asumen ocurran, nunca es bueno. Ahora imagina, hay una tómbola con pelotas negras, rojas, verdes y azules, revuelves un poco la tómbola y en la primera ronda sale una bola roja, luego en la segunda ronda, también sale una roja, y así se mantiene hasta la cuarta ronda, lo más obvio que una persona haría, sería escoger la bola roja, y así pasa, en la cuarta ronda sale una bola roja y en la quinta, sexta, séptima; ya envalentonado, decides apostar todo en la ronda 8 y ganas, decides no retirarte y en la ronda 9 pones todo lo que tienes e incluso parte de lo que no. El resultado, perdiste todo porque asumiste que tu habías encontrado un patrón, cuando lo descubriste, pensaste que se mantendría y no fue así, creíste que te robaron, pero en realidad solo descubriste de forma amarga que lo único seguro en las cosas aleatorias es precisamente eso: todo es aleatorio. Y que pasa cuando hay alguien detrás de todo, un nombre y un rostro al cual culpar cuando todo sale mal, bueno eso ya depende de cada uno.
El ejemplo que mostré antes se adapta muy bien a lo que pasó, hubo tantos indicios que indicaban que el resultado que yo quera iba a pasar, que sin darme cuenta ya asumía que el resultado era auto-evidente. Yo que tantas veces le dije a los demás que nunca deberían de asumir, terminé por engañarme a mi mismo, imaginando cosas que yo quería que pasaran sin tomar en cuenta todo el contexto.
Aun confiado de lo que yo creía, espere una vez más a que las cosas pasaran, y finalmente pasó. No podía creer el resultado, todo era exactamente lo contrario a lo que imaginé, estaba profundamente decepcionado, era la primera vez en que algo me resultó tan extraño, ajeno a mi, a pesar de todo, en esencia era lo mismo, siempre lo fue, de hecho las cosas que yo imaginé en su momento, en realidad eran las cosas extrañas, que si hubieran pasado, simplemente le hubieran quitado lo que originalmente es.
Y bien , el resultado ya lo asimilé un poco y creo que fue lo mejor, aun queda un poco de esperanza, pero prefiero hacer lo que siempre hago: esperar a que ocurra lo de siempre y ya si pasa algo extraordinario, siempre será bienvenido. Ademas, a la mala logre entender a toda la "fanaticada" (a lo que sea)
Para no ponerme en evidencia, solo diré lo siguiente: El 18 de agosto fue un antes y un después.
Por ahora es todo, si lograste llegar hasta aquí, gracias por leer, significa un poco para mi el saber que a alguien más este ahí leyendo lo que pienso.
En el siguiente Post continuaremos con el código de VEncoder 2 y muy probablemente en unos días, invite a alguien más para que haga publicaciones en el blog.
Los leo luego.
8/24/2016 08:57:00 p.m.
offtopic
Vamos a programar #12 - El código de VEncoder 2
El día de hoya vamos a continuar con la revisión del código de vencoder, en los post anterior ya vimos como es que el programa interactua con FFMpeg, el día de hoy veremos un par de funciones que también lo hacen.
El código de la función PrepareWork es el siguiente:
Funcion PrepareWork
El código de la función PrepareWork es el siguiente:
/// <summary>
/// Prepara la linea de comandos y lo necesario para hacer la conversion
/// </summary>
/// <param name="HardWork">Parametro booleano que indica como se debe de hacer el trabajo</param>
/// <remarks>Si HardWork es true, todos lo parametros se pasaran a la conversion; en caso contrario,
/// solo se genera la linea de comandos para usarse de forma externa con FFMPEG</remarks>
private void PrepareWork(bool HardWork = false)
{
List<string> Lote = new List<string>();
List<int> DLote = new List<int>();
if (CHKAll.Checked == true)
{
foreach (ListViewItem LV in lvfilesin.Items)
{
if (LV.Checked)
{
if (CHK2pass.Checked == true)
{
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, "23.974", CBOBitsA.Text, CboFreq.Text, CBOAChannels.Text, TXTSavePath.Text, true, false, false));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, "23.974", CBOBitsA.Text, CboFreq.Text, CBOAChannels.Text, TXTSavePath.Text, false, true, false));
DLote.Add((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds + 1);
}else{
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text, TXTVBit.Text, CBORatio.Text, CBOProfile.Text, CboFreq.Text, CBOBitsA.Text, "44100", CBOAChannels.Text, TXTSavePath.Text, false, false, true));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
}
}
}
}else{
foreach (ListViewItem LV in lvfilesin.Items)
{
if (CHK2pass.Checked == true)
{
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, "23.974", CBOBitsA.Text,CboFreq.Text, CBOAChannels.Text, TXTSavePath.Text, true, false, false));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, "23.974", CBOBitsA.Text, CboFreq.Text, CBOAChannels.Text, TXTSavePath.Text, false, true, false));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
}else{
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,TXTVBit.Text, CBORatio.Text, CBOProfile.Text, CboFreq.Text, CBOBitsA.Text, "44100", CBOAChannels.Text, TXTSavePath.Text,false,false,true));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
}
}
}
if (HardWork)
{
TimerElapsed.Enabled = true;
DoWork(Lote, DLote);
}else{
foreach (String LineCom in Lote)
{
RTBCommandLine.Text = RTBCommandLine.Text + LineCom + "\n";
}
}
}
La función PrepareWork lo que hace es construir la linea de comandos para cada archivo que se encuentre en la lista, dependiendo si se le indicó al programa o no si se quiere hacer la conversion en dos pasadas, crea la linea de comandos lista para usarse.
La función PrepareWork recibe un parámetro del tipo booleano que indica que es lo que se debe de hacer después de crear todas las lineas de comandos. Si el valor del parametro HardWork se establece en true; al terminar de procesar todos los ajustes, llama a la funcón DoWork que es la encargada de iniciar la ejecución FFmpeg.
La construcción de la linea de comandos se realiza a la "mala"; es decir va recorriendo uno a uno los controles para obtener sus propiedades (Text por ejemplo). Ademas de que por ejemplo la cantidad de cuadros por segundo está fija, para evitar eso, en lugar de usar 23.974, podemos usar un TextBox en lugar de 23.974.
Esta función solo "recolecta" todos los datos y lo pone juntos, pero la función que realmente hace el trabajo sucio es la funcion BuildCommandLine.
La función PrepareWork recibe un parámetro del tipo booleano que indica que es lo que se debe de hacer después de crear todas las lineas de comandos. Si el valor del parametro HardWork se establece en true; al terminar de procesar todos los ajustes, llama a la funcón DoWork que es la encargada de iniciar la ejecución FFmpeg.
La construcción de la linea de comandos se realiza a la "mala"; es decir va recorriendo uno a uno los controles para obtener sus propiedades (Text por ejemplo). Ademas de que por ejemplo la cantidad de cuadros por segundo está fija, para evitar eso, en lugar de usar 23.974, podemos usar un TextBox en lugar de 23.974.
Esta función solo "recolecta" todos los datos y lo pone juntos, pero la función que realmente hace el trabajo sucio es la funcion BuildCommandLine.
Funcion BuildCommandLine.
El código de la funcion BuildCommandLine es el siguiente:
/// <summary>
/// Genera la linea de comados para usarse con FFMPEG
/// </summary>
/// <param name="InFile">Archivo de entrada</param>
/// <param name="VCodec">Codec de video a usarse</param>
/// <param name="VideoSize">Resolucion del video</param>
/// <param name="VideoBit">Velocidad de bits del video</param>
/// <param name="VAspect">Ratio para el video</param>
/// <param name="VProfile">Perfil para usarse en el video</param>
/// <param name="FrameRate">Cantidad de cuadros por segundo para el video</param>
/// <param name="AudioBit">Veocidad de Bits para el audio</param>
/// <param name="AudioFreq">Frecuencia de muestreo para el audio</param>
/// <param name="AChannels">Numero de canales para el audio</param>
/// <param name="OutPath">Donde se gurdara la salida; debe de ser una ruta a un carpeta</param>
/// <param name="ForFirstPass">La linea de comandos se hará para la primera pasada de dos</param>
/// <param name="AfterPass1">La Linea de comandos se hará para la segunda pasada</param>
/// <param name="SinglePass">La linea de comandos se hara para una unica pasada</param>
private string BuildCommandLine(string InFile,string VCodec,string VideoSize,string VideoBit,string VAspect,string VProfile,string FrameRate,string AudioBit,
string AudioFreq, string AChannels,string OutPath,bool ForFirstPass= false,bool AfterPass1=false,bool SinglePass= true)
{
if (ForFirstPass){
return string.Concat("-y -i ","\"",InFile,"\""," -c:v ",VCodec," -s ",VideoSize, " -b:v ",VideoBit,"k -aspect ",VAspect," -profile:v ",VProfile," -threads 0 -r ",FrameRate,
" -level 4.1 -crf 22 -f mp4 -pass 1 -an NUL");
}
if(AfterPass1){
return string.Concat("-y -i ","\"",InFile,"\""," -c:v ",VCodec," -s ",VideoSize, " -b:v ",VideoBit,"k -aspect ",VAspect," -profile:v ",VProfile," -threads 0 -r ",FrameRate,
" -level 4.1 -crf 22 -f mp4 -pass 2 -c:a libvo_aacenc -b:a ",AudioBit,"k -ar ", AudioFreq," -ac ",AChannels," \"",OutPath,"\\",GetFileName(InFile),".mp4","\"");
}
if (SinglePass) {
return string.Concat("-y -i ","\"",InFile,"\""," -c:v ",VCodec," -s ",VideoSize, " -b:v ",VideoBit,"k -aspect ",VAspect," -profile:v ",VProfile," -threads 0 -r ",FrameRate,
" -level 4.1 -crf 22 -f mp4 -c:a libvo_aacenc -b:a ",AudioBit,"k -ar ", AudioFreq," -ac ",AChannels," \"",OutPath,"\\",GetFileName(InFile),".mp4","\"");
}else{
return "Test";
}
}
La funcion BuildCommandLine se encarga de crear la linea de comandos para usarse con FFMpeg, recibe 14 parametros y regresa un valor del tipo string con los datos listos para usarse.
La forma mas adecuada para manejar para que tipo de "pasada" la linea de comandos es crear una enumeracion y definir solo los casos posibles por ejemplo:
Y solo restaría adaptar la condiciones para cada caso.
Bien, por ahora es todo, recomiendo revisar el código para que entiendas de lo que hablo.
Los leo luego.
- InFile. Es el archivo para el cual queremos crear la linea de comandos.
- VCodec. Es el codec que vamos a usar para el archivo, aunque ahora solo esta configurado para que la linea de comandos sea compatible con el codec X264, mas adelante la modificaremos para agregar soporte a otros codecs (Xvid,MPEG4, etc)
- VideosSize. Es la resolución que tendrá el vídeo, puede ser cualquier valor que tenga la forma WWWWxHHHH, donde W es el largo y H es el ancho (1280x720 es un valor valido).
- VideoBit. Es la cantidad de bits que se usaran por segundo.
- VAspect. Indica el aspecto que deberá tener el vídeo (16:9).
- VProfile. Es el tipo de perfil que debe de tener el vídeo.
- FrameRate. Sirve para indicar la cantidad de cuadros por segundo que se deben de usar.
- AudioBit.Cantidad en bits para el audio
- AudioFreq. La frecuencia del audio en Hz.
- AChannels. Cantidad de canales de audio que debe de tener el vídeo.
- OutPath. La ruta en donde se guardaran los vídeos.
- ForFirstPass. Este parámetro indica como se crea la linea de comandos, si el parámetro es true, se crea la linea de comandos para la primer pasada
- AfterPass1. Este parámetro indica como se crea la linea de comandos, si el parámetro es true, se crea la linea de comandos para la segunda pasada
- SinglePass. Crea la linea de comandos para proceso de una sola pasada.
La forma mas adecuada para manejar para que tipo de "pasada" la linea de comandos es crear una enumeracion y definir solo los casos posibles por ejemplo:
private enum CommandType
{
ForPass1,
ForPass2,
ForSinglePass
}
Y solo restaría adaptar la condiciones para cada caso.
Bien, por ahora es todo, recomiendo revisar el código para que entiendas de lo que hablo.
Los leo luego.
8/13/2016 02:42:00 p.m.
Convertir videos
,
csharp
,
ffmpeg
,
GUI
,
how to
,
interesante
,
programacion
,
Vencoder
,
video
Vamos a programar #11 - El código de VEncoder 2
El día de hoy vamos a continuar revisando el código de VEncoder2, la vez anterior ya habíamos revisado el código de las primeras 4 funciones. Normalmente vemos las funciones de 2 en 2 pero hoy, vamos a ver un poco mas debido a que algunas de las funciones son realmente simples.
Lo único que hace esta función es regresar el valor equivalente al porcentaje actual, recibe dos parámetros del tipo Single ValorActual y ValorTotal, y después solo hace la división entre ValorActual y ValorTotal, luego lo multiplica por 100, el valor resultante es el porcentaje de ValorActual con respecto a ValorTotal.
La funcion GetFileName sirve para obtener el nombre de archivo de una ruta completa.es decir, si tenemos la ruta a un archivo cómo la siguiente: "c:\users\Ra\Desktop\Mi video.mp4" al usar la función obtenemos "Mi video.mp4". Esta función recibe un parámetro del tipo string, después la separa usando la función "split", la función split recibe como parámetro del tipo char en este caso es "(char)92", lo que hace es convertir el numero 92 a el tipo char y es equivalente a "\" (backslash). Se usa la variable TMPData para almacenar el resultado de la función split que aplicamos sobre el parámetro de entrada. Finalmente solo regresa el ultimo valor de la matriz.
A lo mejor puedes pensar que haciendo eso nos complicamos demasiado porque dentro de las fucniones que vienen incluidas en .NET, dentro del espacio de nombre System.IO hay una funcion que hace exactamente lo mismo. Path.GetFileName hace lo mismo, pero debido a que es codigo que vengo arrastrando desde VB6 (solamente adaptado a C#), preferi incluirlo, pero sientete libre de usar la función que mas quieras.
La función GetPositionFromFFLine extrae el tiempo actual de la conversion, para entender como funciona, primero echemos un vistazo a una linea de resultado de FFmpeg.
Primero comprueba que la línea empiece con "FRAME", luego, separa la parte del tiempo, en este caso al usar la funcion split con parametro "(char)61" (simbolo de igual "="), luego convierte el tiempo a segundos y lo regresa como resultado.
Obviamente esta funcion no es a prueba de errores, de hecho aumimos que el tiempo estará formateado de la forma HH.MM.SS.CC, pero sei en algun momento alguien intentara convertir un vide de 100 horas de duracion, VEncoder simplemente marcaría un error.
Igual que la funcion anterior, no esta optimizada, lo ideal seria usar expresiones regulares, pero por ahora esto, más adelante actualizaremos todas las funciones.
Por ahora es todo. Los leo luego.
Funcion Porcentaje
El código de la funcion porcentaje es el siguiente:/// <summary>
/// Calcula porcentaje
/// </summary>
/// <param name="ValorActual">Valor actual para hacer el calculo</param>
/// <param name="ValorTotal">Valor total para hacer el calculo</param>
/// <returns>Un numero que representa el valor del porcentaje</returns>
private Single Porcentaje(Single ValorActual, Single ValorTotal)
{
return (ValorActual / ValorTotal) * 100;
}
Lo único que hace esta función es regresar el valor equivalente al porcentaje actual, recibe dos parámetros del tipo Single ValorActual y ValorTotal, y después solo hace la división entre ValorActual y ValorTotal, luego lo multiplica por 100, el valor resultante es el porcentaje de ValorActual con respecto a ValorTotal.
Funcion GetFileName
El código de la función GetFileName es el siguiente:
/// <summary>
/// Obtiene el nombre de archivo desde una ruta completa
/// </summary>
/// <param name="FileName">La ruta de la cual se quiere obtener el nombre de archivo</param>
/// <returns>Un string con el equivalente al nombre de archivo</returns>
/// <remarks>El valor devuelto no incluye la extension</remarks>
private string GetFileName(string FileName)
{
string[] TMPData = FileName.Split((char)92);
return TMPData[TMPData.Length - 1];
}
La funcion GetFileName sirve para obtener el nombre de archivo de una ruta completa.es decir, si tenemos la ruta a un archivo cómo la siguiente: "c:\users\Ra\Desktop\Mi video.mp4" al usar la función obtenemos "Mi video.mp4". Esta función recibe un parámetro del tipo string, después la separa usando la función "split", la función split recibe como parámetro del tipo char en este caso es "(char)92", lo que hace es convertir el numero 92 a el tipo char y es equivalente a "\" (backslash). Se usa la variable TMPData para almacenar el resultado de la función split que aplicamos sobre el parámetro de entrada. Finalmente solo regresa el ultimo valor de la matriz.
A lo mejor puedes pensar que haciendo eso nos complicamos demasiado porque dentro de las fucniones que vienen incluidas en .NET, dentro del espacio de nombre System.IO hay una funcion que hace exactamente lo mismo. Path.GetFileName hace lo mismo, pero debido a que es codigo que vengo arrastrando desde VB6 (solamente adaptado a C#), preferi incluirlo, pero sientete libre de usar la función que mas quieras.
Funcion GetPositionFronFFLine.
El código de la funcion GetPositionFromFFLine es el siguiente:
/// <summary>
/// Obtiene el valor de tiempo desde una linea de resultado de ffmpeg
/// </summary>
/// <param name="FFText">Linea proveniente de FFMPEG</param>
/// <returns>Regresa un numero con el valor en segundos</returns>
private Single GetPositionFromFFline(string FFText)
{
try
{
if (FFText.StartsWith("FRAME", StringComparison.OrdinalIgnoreCase)) {
string PreResult;
PreResult = FFText.Split((char)61)[5].ToString().Trim();
return (Single)TimeSpan.Parse(PreResult.Remove(PreResult.Length - 7, 7).Trim()).TotalSeconds;
}else{
return 0;
}
} catch (Exception) {
return 1;
}
}
La función GetPositionFromFFLine extrae el tiempo actual de la conversion, para entender como funciona, primero echemos un vistazo a una linea de resultado de FFmpeg.
frame= 2528 fps=167 q=27.0 size= 5246kB time=00:01:43.27 bitrate= 416.1kbits/sLa anterior es una linea de ffmpeg cuando esta haciendo una conversión, ahora si la observamos bien, vemos que esta compuesta por 6 partes:
- frame= 2528, indica el frame actual de la conversión.
- fps=167, cantidad de cuadros por segundo a la que se hace la conversión.
- q=27.0, calidad
- size = 5246, tamaño en Kbytes.
- time = 00:01:43.27, tiempo actual de la conversión
- bitrate= 416.1kbits/s , bitrate actual.
Primero comprueba que la línea empiece con "FRAME", luego, separa la parte del tiempo, en este caso al usar la funcion split con parametro "(char)61" (simbolo de igual "="), luego convierte el tiempo a segundos y lo regresa como resultado.
Obviamente esta funcion no es a prueba de errores, de hecho aumimos que el tiempo estará formateado de la forma HH.MM.SS.CC, pero sei en algun momento alguien intentara convertir un vide de 100 horas de duracion, VEncoder simplemente marcaría un error.
Igual que la funcion anterior, no esta optimizada, lo ideal seria usar expresiones regulares, pero por ahora esto, más adelante actualizaremos todas las funciones.
Por ahora es todo. Los leo luego.
Vamos a programar #10 - El código de VEncoder 2
El día de hoy vamos a continuar con el código fuente de VEncoder 2, la última vez, analizamos las primeras dos funciones, estas se encargaban de cargar y guardar los archivos VEPX, han pasado algunos días, y si no has revisado el código fuente aún, sugiero que lo revises antes de continuar con el resto.
Función FixTimeDuration
El código de la funcion FixDuraction es el siguiente:private int FixTimeDuration(int Duration)
{
if (Duration < 3600)
{
return Duration + 1;
}else{
return Duration + 60;
}
}
Cuando obtenemos el tiempo usando la librería MediaInfoNet, a veces regresa el tiempo de forma redondeada hacia abajo, por ejemplo; un vídeo que dura 01:05:35.21, en algunas ocasiones si es que el vídeo no tiene la información correcta, regresara solo el valor "entero" de los segundos; tómese el ejemplo anterior, el tiempo 01:05:35.21 al pedirle a MediaInfoNet regresaría el valor de 01:05:35.00; al asignar las propiedades a los controles ProgressBar, sin el ajuste al pasar del tiempo 01:05:35.00 a 01:05:35.01, provocaría un error.
La función FixTimeDuration, corrige el error agregando 1 o 60 segundos, si la duración del vídeo es menor a una hora, solo agrega un segundo; en caso contrario agrega 60 segundos a la duración total del vídeo.
Ahora te preguntaras: ¿por que 60, por que 1?, debido a que pierde precisión cuando el tiempo pasa de una hora y redondea los minutos hacia abajo; es decir, el tiempo 01:01:40.32, directamente MediaInfoNet, lo devuelve como 01:01:00.00. Entonces para evitar eso, usamos esta función.
La función recibe un parámetro del tipo int que es equivalente a la duración del vídeo en segundos. Regresa un valor del tipo int que es el equivalente a la duración del vídeo en segundos +1 o +60 dependiendo del caso.
La forma de solucionarlo es obtener primero el tiempo usando el mismo FFMpeg, analizar la línea en donde nos proporciona el tiempo y solo asignarlo, pero los cambios los veremos después, ahora pasemos a la siguiente función.
La función FixTimeDuration, corrige el error agregando 1 o 60 segundos, si la duración del vídeo es menor a una hora, solo agrega un segundo; en caso contrario agrega 60 segundos a la duración total del vídeo.
Ahora te preguntaras: ¿por que 60, por que 1?, debido a que pierde precisión cuando el tiempo pasa de una hora y redondea los minutos hacia abajo; es decir, el tiempo 01:01:40.32, directamente MediaInfoNet, lo devuelve como 01:01:00.00. Entonces para evitar eso, usamos esta función.
La función recibe un parámetro del tipo int que es equivalente a la duración del vídeo en segundos. Regresa un valor del tipo int que es el equivalente a la duración del vídeo en segundos +1 o +60 dependiendo del caso.
La forma de solucionarlo es obtener primero el tiempo usando el mismo FFMpeg, analizar la línea en donde nos proporciona el tiempo y solo asignarlo, pero los cambios los veremos después, ahora pasemos a la siguiente función.
Funcion TiempoRestante
El codigo de la funcion TiempoRestante es el siguiente: private Single TiempoRestante(Single Porciento,int TransTime)
{
Single Z;
//z = tiempo gastado por cada 1%
if (Porciento == 0)
{
Porciento = 1;
}
Z = TransTime / Porciento;
return (100 - Porciento) * Z;
}
Esta funcion se encarga de obtener el tiempo restante para un proceso, para usarlo, se le pasan dos argumentos; uno del tipo Single y una del tipo int. Es realmente simple, pero hablemos un poco más acerca de esto. como muchos sabrán, cuando hablamos de cálculos de tiempo, en computación no suelen ser tan fáciles. Y para este caso no es la excepción.
Para empezar analicemos un poco cual es la "ecuación" para calcular el tiempo restante. Supongamos que tenemos hambre y nos da por comer manzanas, comemos 1 y nos sentimos 20% satisfechos, para saber cuantas manzanas necesitamos comer, lo que debemos de hacer es dividir lo que nos falta para saciarnos entre la cantidad que nos llena una manzana; retomando la metáfora anterior, si una manzana nos llenó en un 20%, significa que nos resta el 80%, dividiendo queda que necesitamos 4 manzanas más para saciarnos.
Para la función es relativamente lo mismo, lo primero que hacemos es obtener cuanto tiempo nos llevo avanzar un 1%, después lo que hacemos es descontar el porcentaje actual del 100% y lo multiplicamos por el tiempo que lleva cada 1%, ¡Simple!., lo sería pero no todo en la vida es fácil, al igual que si tratáramos de determinar cuantas manzanas bastaran para saciar tu hambre, podemos decir un estimado, pero no una cantidad exacta, lo mismo pasa con el calculo del tiempo para procesos de este tipo, podemos estimar el tiempo aproximado restante, pero no tendremos una certeza del 100%. Ademas cuando se hacen las conversiones; en el caso concreto de VEencoder2, se le puede indicar al programa que queremos hacer la conversión en dos pasos. Y siempre la primera es hasta 10 veces más rápida, entonces determinar el tiempo necesario para todo el proceso de conversión en un solo archivo resulta difícil, ahora para 10 archivos será peor. Entonces esta funcion cumple su objetivo ya que devuelve el tiempo estimado del proceso que se ejecuta, en nuestra aplicación, regresa el tiempo para cada "pasada". Resulta en un "placebo" para el usuario, pero sirve ya que en mi caso (supongo que no soy el unico) saber más o menos cuanto tiempo falta para algo, ayuda un poco.
Bueno por ahora es todo en el siguiente post continuaremos con las revisiones a las funciones de VEncoder2, recuerda que recomiendo revisar el código fuente para que resulte más fácil entender de lo que hablo.
Los leo luego
Para empezar analicemos un poco cual es la "ecuación" para calcular el tiempo restante. Supongamos que tenemos hambre y nos da por comer manzanas, comemos 1 y nos sentimos 20% satisfechos, para saber cuantas manzanas necesitamos comer, lo que debemos de hacer es dividir lo que nos falta para saciarnos entre la cantidad que nos llena una manzana; retomando la metáfora anterior, si una manzana nos llenó en un 20%, significa que nos resta el 80%, dividiendo queda que necesitamos 4 manzanas más para saciarnos.
Para la función es relativamente lo mismo, lo primero que hacemos es obtener cuanto tiempo nos llevo avanzar un 1%, después lo que hacemos es descontar el porcentaje actual del 100% y lo multiplicamos por el tiempo que lleva cada 1%, ¡Simple!., lo sería pero no todo en la vida es fácil, al igual que si tratáramos de determinar cuantas manzanas bastaran para saciar tu hambre, podemos decir un estimado, pero no una cantidad exacta, lo mismo pasa con el calculo del tiempo para procesos de este tipo, podemos estimar el tiempo aproximado restante, pero no tendremos una certeza del 100%. Ademas cuando se hacen las conversiones; en el caso concreto de VEencoder2, se le puede indicar al programa que queremos hacer la conversión en dos pasos. Y siempre la primera es hasta 10 veces más rápida, entonces determinar el tiempo necesario para todo el proceso de conversión en un solo archivo resulta difícil, ahora para 10 archivos será peor. Entonces esta funcion cumple su objetivo ya que devuelve el tiempo estimado del proceso que se ejecuta, en nuestra aplicación, regresa el tiempo para cada "pasada". Resulta en un "placebo" para el usuario, pero sirve ya que en mi caso (supongo que no soy el unico) saber más o menos cuanto tiempo falta para algo, ayuda un poco.
Bueno por ahora es todo en el siguiente post continuaremos con las revisiones a las funciones de VEncoder2, recuerda que recomiendo revisar el código fuente para que resulte más fácil entender de lo que hablo.
Los leo luego
8/01/2016 05:15:00 p.m.
aprende
,
Convertir videos
,
csharp
,
ffmpeg
,
interesante
,
programacion
,
Vencoder
,
video
Vamos a programar #9 - El Código de VEncoder 2.
El día de hoy; y como lo prometí hace algunos días, les traigo el código fuente de VEncoder 2.
Las entregas se van a repartir en las siguientes:
El resto del código que no está dentro de ninguna de estas, es código propio del formulario o de los controles; eventos constructores y todo lo demás no tienen ninguna Region definida.
Para VEncoder2 solo usaremos 4 variables globales
Las entregas se van a repartir en las siguientes:
- El código de VEncoder2.
- Porqué funciona cómo funciona.
- Agregando funciones que necesito.
- Errores, Intentando arreglarlos.
El Código de VEncoder2.
Lo primero que vamos a hacer es echarle un vistazo al código de fuente, tratare de explicar un poco que es lo que hace cada parte del código. Algo importante que debo mencionar es que el código ya tiene "sus ayeres", originalmente estaba planeado para uso personal, así que el código puede estar "sucio". Si eres algo curioso, de seguro te preguntaras ¿VEncoder2, donde esta el 1?, la respuesta es simple, nunca vio la luz del sol, y no porque fuera malo u otra cosa, simplemente no se me ocurrió que le resultara interesante a alguien más a parte de mí, de hecho la idea de hacer un blog surgió a partir de alguien a quien siempre le agradeceré... Pero eso es otra historia, sigamos en los que estábamos.
![]() |
| Inicialmente estaba pensado para convertir los vídeos y que fueran compatibles con el PSP y PSvita |
Abriendo el proyecto.
Lo primero que debemos de hacer es descargar el proyecto (aquí) y abrirlo con #Develop o visual studio. Para poder usrlos, primero debemos de agregar un par de referencias a los siguientes ensamblados: MediainfoNet.dll y ListviewEx.dll (incluidas en la descarga). La primer librería solo se trata de un "wrapper" para poder usar la librería MediaInfo.dll que sirve para obtener la información detallada de los archivos multimedia (Resolucion, BitRate, etc) y la segunda es una versión extendida del ListView, posee algunas funciones extra a un ListView normal, estas las veremos mas adelante.
![]() |
| Al inicio, puede aparecer este mensaje de error. |
Para agregar las referencias solo hay que ir a "Proyecto>Agregar referencia" y en la ficha "Examinar", buscar la carpeta debug que viene incluida en el archivo que descargaste y seleccionar "ListViewEx.dll y MediaInfoNet.dll"
El código está dividido en dos regiones:
- Declaraciones (globales)
- Funciones
Para VEncoder2 solo usaremos 4 variables globales
#region Declaraciones private bool IsConvertingSomething = false;
private bool RequestForCancel = false; private int ElapsedTimeGlobal = 0; private int ElapsedTimeCurrent = 0; #endregion
Los nombres son realmene explicitos, entonces creo que no hace falta mencionar cada una, de todos modos sobre la marcha, veremos que tipo de valores se le asignan a cada una.
Función SaveProfile
Para no hacer muy largo el post,el día de hoy solo analizaremos las primeras dos funciones que nos encontraremos al abrir el proyecto. La primera funcion es la que se encarga de abrir un archivo VEPX con los ajustes para la conversion./// <summary>
/// Funcion que se encarga de guardar loa ajustes en archivo VEPX
/// </summary>
/// <param name="XmlPath">Ruta en donde se guardara</param>
/// <param name="ProfileName">Nombre que se usara para identificar el archivo</param>
private void SaveProfile(string XmlPath, string ProfileName)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = "\t";
using (XmlWriter XWriter = XmlWriter.Create(XmlPath, settings))
{
XWriter.WriteStartDocument();
XWriter.WriteComment("Archivo de preset para VEncoder 2");
XWriter.WriteStartElement("Perfil");
XWriter.WriteAttributeString("v", "1");
XWriter.WriteAttributeString("appname", "VEncoder2");
XWriter.WriteElementString("Name", ProfileName);
XWriter.WriteElementString("WH", CboResolucion.Text);
XWriter.WriteElementString("VBit", TXTVBit.Text);
XWriter.WriteElementString("Ratio", CBORatio.Text);
XWriter.WriteElementString("Profile", CBOProfile.Text);
XWriter.WriteElementString("Nivel", CboLevel.Text);
XWriter.WriteElementString("ASample", CboFreq.Text);
XWriter.WriteElementString("ABit2", CBOBitsA.Text);
XWriter.WriteElementString("Channel", CBOAChannels.Text);
XWriter.WriteElementString("ExtraParams", TXTExtraparams.Text);
XWriter.WriteEndElement();
XWriter.WriteEndDocument();
}
}
Lo que va a hacer es leer los controles y guardar sus valores en un archivo XML, para eso, usamos XMLWritter. De hecho lo hacemos de forma inpractica, pero debido a que siempre van a ser los valores de los mismos controles, ademas de que no son muchos, entonces podemos hacer de forma explicita, indicando el nombre del control cuyo valor queremos obtener y simplemente guardarlo. Por ejemplo, XWriter.WriteElementString("WH", CboResolucion.Text); lo que hace es guardar en un elemento XML llamado "WH" el texto contenido en CboResolution. Para poder entender un poco mejor a que se hace referencia, recomiendo echarle un vistazo al proyecto, cada control tiene su nombre, entonces una vez que te familiarices con los nombres, sera más rápido entender el código.
Funcion LoadProfile
La siguiente función hace lo contrario, en lugar de guardar, está lee desde un archivo existente./// <summary>
/// Carga un archivo XML con los ajustes para realizar la cadificacion.
/// </summary>
/// <param name="InXMLFile">Ruta al archivo que se debe de leer</param>
private void LoadProfile(string InXMLFile)
{
using (XmlReader XMLR = new XmlTextReader(InXMLFile))
{
XMLR.ReadToFollowing("Name");
lnklblLoadProfile.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("WH");
CboResolucion.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("VBit");
TXTVBit.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("Ratio");
CBORatio.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("Profile");
CBOProfile.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("Nivel");
CboLevel.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("ASample");
CboFreq.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("ABit2");
CBOBitsA.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("Channel");
CBOAChannels.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("ExtraParams");
TXTExtraparams.Text = XMLR.ReadElementContentAsString();
}
}
Verás que el código es realmente similar al anterior, de hecho lo es, pero para poder entenderlo es necesario estar familiarizado con los nombres de los controles.
Creo que por ahora es todo, sirve que le echas in vistazo al código y ya despues seguiremos con la explicación a lo demás.
Los leo luego.
Creo que por ahora es todo, sirve que le echas in vistazo al código y ya despues seguiremos con la explicación a lo demás.
Los leo luego.
7/30/2016 03:26:00 p.m.
aprende
,
Convertir videos
,
csharp
,
DIY
,
GUI
,
how to
,
interesante
,
programacion
,
Vencoder
,
video
El vídeo correcto #2 - Cómo funciona VEncoder.
El día de hoy le quiero mostrar como es que funciona VEncoder 2, esto en lo que arreglo un poquito el código fuente para poder publicarlo y que cada quien cree su propia versión.
De forma corta, que es lo que hace cada botón:
Paso 1. Abriendo los archivos.
El primer paso y el más obvio es agregar los archivos que se quieren convertir. Cómo estamos usando FFMpeg, el tipo de de archivos que podemos convertir es muy alto, pero para no complicarnos veremos los más comunes.
Los archivos mas comunes, suelen venir con extensiones:
- MP4
- MKV
- WMV
- AVI
- MPEG
Para agregarlos hay que usar el botón que dice "Abrir archivos" se abrirá la ventana de selección de archivos. Solo tenemos que seleccionar los que queramos convertir.
![]() |
| En mi caso voy a convertir algunos vídeos que grabe con una cámara relativamente vieja y que no son compatibles con ninguna de las pantallas que tengo. |
La intefaz está dividida en dos partes, la primera esta diseñada para poder gestionar los archivos que se agregan, es posible quitar algunos de los archivos (si cambias de opinion), seleccionar solo algunos para convertirlos en ese momento o simplemente quitar todo.
![]() |
| La parte del control de archivos consiste en 5 botones y una casilla de verificación. |
- "Abrir Archivos", muestra el dialogo de selección de archivos y los agrega a la lista de conversión.
- "Quitar todos", limpia toda la lista de archivos.
- "Quitar marcados", quita de la lista solo los archivos que tengan marca de selección.
- "Marcar todos", le pone marca de selección a todos los archivos.
- "Invertir selección", Si un archivo de la lista tiene marca de selección, la quita; en caso contrario se la pone.
- "Convertir solo los marcados", cuando se le dice la programa que empiece ala conversion, solo hará la de aquellos que tienen marca selección.
"Ajustando" los ajustes.
Para realizar la conversión, primero hay que poner los valores adecuados para que al final el vídeo sea compatible.
Una configuración compatible con la mayoría de las pantallas del mercado es la siguiente;
Y darán como resultado lo siguiente:
La linea de comandos solo es ilustrativa. Cuando ya se modificaron los ajustes a los que cada uno necesita, solo basta con presionar el botón "Empezar".
Una configuración compatible con la mayoría de las pantallas del mercado es la siguiente;
![]() |
| Los ajustes como los de la imagen funcionan. |
Y darán como resultado lo siguiente:
-y -i "C:\MI Video.mp4" -c:v libx264 -s 1280x720 -b:v 4000k -aspect 16:9 -profile:v high -threads 0 -r 23.974 -level 4.1 -crf 22 -f mp4 -pass 1 -an NUL -y -i "C:\Mi video.mp4" -c:v libx264 -s 1280x720 -b:v 4000k -aspect 16:9 -profile:v high -threads 0 -r 23.974 -level 4.1 -crf 22 -f mp4 -pass 2 -c:a libvo_aacenc -b:a 128k -ar 44100 -ac 2 "C:\Users\Ra\Desktop\MI Video.mp4"
La linea de comandos solo es ilustrativa. Cuando ya se modificaron los ajustes a los que cada uno necesita, solo basta con presionar el botón "Empezar".
Agilizando el trabajo.
Cómo lo mas seguro es que vas a realizar mas de una vez el mismo tipo de conversión, debe de haber una forma de guardar una configuración para poder usarla después. La solución fue crear archivos de "perfil", en el se guardan las configuraciones para solo poder cargarlas.
Si descargaste VEncoder, al momento de extraer el paquete, de seguro viste un par de archivos con extensión VEPX. Estoso son configuraciones guardadas, para usarlas, en la pantalla principal, deberás de hacer clic en en las letras azules que por default dicen:"Archivo de perfil: ninguno", se abrirá un dialogo con filtro para los archivos VEPX, solo debes de escoger uno válido para usarlo.
Las ventajas son simples, ahorra el trabajo de cambiar los ajustes cada vez.
Pero que son los archivos VEPX.
Los archivos VEPX en realidad son archivos XML con los ajustes, el código de VEncoder se encarga de asignar cada valor al correspondiente para la conversión.
De hecho si abres el archivo con Notepad++ se verá similar a este.
La parte programacional la veremos en el siguiente Post, Solo como recordatorio, el programa aun es BETA, por lo que los fallos aun estan, pero no te preocupes, en el siguiente post empezaremos a analizar y a depurar el codigo de VEncoder2.
Por ahora es todo, los leo luego.
De hecho si abres el archivo con Notepad++ se verá similar a este.
<?xml version="1.0" encoding="utf-8"?> <!--Archivo de preset para VEncoder 2--> <Perfil v="1" appname="VEncoder2"> <Name>HD Medium</Name> <WH>1280x720</WH> <VBit>4096</VBit> <Ratio>16:9</Ratio> <Profile>high</Profile> <Nivel>41</Nivel> <ASample>44100</ASample> <ABit2>128</ABit2> <Channel>2</Channel> <ExtraParams /> </Perfil>
La parte programacional la veremos en el siguiente Post, Solo como recordatorio, el programa aun es BETA, por lo que los fallos aun estan, pero no te preocupes, en el siguiente post empezaremos a analizar y a depurar el codigo de VEncoder2.
Por ahora es todo, los leo luego.
7/28/2016 03:00:00 p.m.
Convertir videos
,
csharp
,
divertido
,
DIY
,
ffmpeg
,
GUI
,
how to
,
programacion
,
Vencoder
,
video
El vídeo correcto.
Un ligero problema.
Hace tiempo conseguí una smart TV para poder ver mas cómodamente algunos vídeos que conseguía del internet, tiempo después conseguí otra Tv que planeaba usar cómo monitor, pero por cuestiones "practicas" terminó siendo usada para lo mismo que la primera. En ambas tv´s, hay puertos USB y las dos soportaban la reproducción de medios, vídeos, fotos y música.La primera vez que intente reproducir vídeo que previamente ya había visto en la smart TV, me llevé una sorpresa al ver que algunos archivos no eran compatibles con la otra pantalla, eso a pesar de que las dos soportan full HD.
La solución mas obvia es volver a convertir los videos y adaptarlo para que sean compatibles con ambas televisiones. Ahora solo quedaba una pregunta: ¿Qué programa usar?. Cuando buscas en internet, aparecerán infinidad de programas, alguno gratis, otros de paga y otros con malware :|, al buscar entre los programas freeware, me di cuenta que la mayoría usaban FFmpeg cómo "motor" de conversión, entonces decidí buscar directamente ese programa para ver que pasaba.
FFmpeg la mejor solución.
La mejor solución era usar FFmpeg directamente, pero solo que habia un pequeño problema, ¡Utiliza linea de comandos!, es decir que hay que configurar cada vez que se quiera hacer una conversion, aun si se trata de hacer un pequeño cambio, hay que editar manualmente la linea de comandos y despues pasarla a ffmpeg.
La solución fue hacer un programa en c# que construyera la línea de comandos y la pasara a FFmpeg.
Les presento VEncoder 2 beta. otra GUI que se encargará de hacer el trabajo sucio por nosotros. Cómo de costumbre haré un tutorial para que tu mismo hagas una versión, pero por ahora le comparto la mía.
Con está GUI podemos hacer los siguiente:
El código lo publicaré después, si tienes una pantalla de gran resolución, harbas visto que el blog se descuadra, entonces primero arreglare eso y ya después publicare el código fuente.
En el siguiente post explicare un poco el funcionamiento de esta GUI, luego el código fuente y funciones "avanzadas" de FFMpeg.
Hay que recordar que está en modo beta y las instrucciones no las pongo para que así, dexcubras tu mismo que es lo que está bien y que es lo que está mal, ya despues veremos cómo usarlo y cómo agregar las funciones que necesitamos desde el código fuente.
Por ahora es todo. Los leo luego
La solución fue hacer un programa en c# que construyera la línea de comandos y la pasara a FFmpeg.
Les presento VEncoder 2 beta. otra GUI que se encargará de hacer el trabajo sucio por nosotros. Cómo de costumbre haré un tutorial para que tu mismo hagas una versión, pero por ahora le comparto la mía.
Con está GUI podemos hacer los siguiente:
- Realizar conversiones en lote.
- Crear la linea de comandos para después usarla con FFmpeg direcamente.
- Cambiar los ajuste de la conversión de manear rápida.
- Guardar configuraciones en archivos *.vepx para poder usarlos después.
Que necesitas:
- Un equipo con windows 7 o superior
- La aplicación, la puedes descargar de aquí
- FFMPEG de 32 o 64 bits, dependiendo de que versión de windows uses.
![]() |
| FFmpeg y todos los archivos de la aplicación deben de estar juntos. |
En el siguiente post explicare un poco el funcionamiento de esta GUI, luego el código fuente y funciones "avanzadas" de FFMpeg.
Hay que recordar que está en modo beta y las instrucciones no las pongo para que así, dexcubras tu mismo que es lo que está bien y que es lo que está mal, ya despues veremos cómo usarlo y cómo agregar las funciones que necesitamos desde el código fuente.
Por ahora es todo. Los leo luego
7/24/2016 01:56:00 p.m.
aprende
,
csharp
,
DIY
,
ffmpeg
,
GUI
,
how to
,
interesante
,
programacion
,
video
Una manita de gato.
Hola a todos. El día de hoy solo escribo para avisar que el blog está en etapa de diseño, habrán cambios y algunas secciones no funcionaran o no lo harán de manera correcta. Más no te preocupes, solo será por unos días, después volveremos a la normalidad.
Gracias a todos por la paciencia.
7/18/2016 08:58:00 a.m.
offtopic
Vamos a programar #8 - Inútil-apps #1 - Max7219 + arduino.
El dia de hoy terminaremos de hacer nuestro proyecto. Ya hicimos la parte del hardware, la parte de windows y ahora solo nos resta la parte del software para Arduino.
¿Que necesitamos?
El dia de hoy solo vamos a usar el arduino IDE, ademas de que el arduino que vayamos a usar, deberá de estar conectado al pc para poder subir el programa.
Adicionalmente usaremos la librería MaxMatrix. Una vez que la descargamos y la extrajimos, debemos de instalarla antes de poder usarla, para eso primero la copiaremos a la carpeta que el IDE de arduino crea para nosotros. Generalmente será "C:\Users\XXX\Documents\Arduino\libraries", en la carpeta "libraries" copiaremos la carpeta "MaxMatrix" ya descomprimida.
Despues en el IDE arduino procedremos a instalarla, para eso haremos clic en el menú "Programa>Incluir Liberia>Gestionar Librerías", nos aparecerá una ventana:
Buscaremos el elemento que diga "MaxMatrix Built-In" y haremos clic en instalar.
Adicionalmente usaremos la librería MaxMatrix. Una vez que la descargamos y la extrajimos, debemos de instalarla antes de poder usarla, para eso primero la copiaremos a la carpeta que el IDE de arduino crea para nosotros. Generalmente será "C:\Users\XXX\Documents\Arduino\libraries", en la carpeta "libraries" copiaremos la carpeta "MaxMatrix" ya descomprimida.
Despues en el IDE arduino procedremos a instalarla, para eso haremos clic en el menú "Programa>Incluir Liberia>Gestionar Librerías", nos aparecerá una ventana:
![]() |
| Cuando se va instalar por primera vez, aparecerá un botón que dirá "Instalar", cuando ya esta instalada, solo aparece el nombre de la librería. |
El código
Ahora veremos el código que hace funcionar las cosas:
#include <MaxMatrix.h>
#include <avr/pgmspace.h>
PROGMEM const unsigned char CH[] = {
3, 8, B00000000, B00000000, B00000000, B00000000, B00000000, // space
1, 8, B01011111, B00000000, B00000000, B00000000, B00000000, // !
3, 8, B00000011, B00000000, B00000011, B00000000, B00000000, // "
5, 8, B00010100, B00111110, B00010100, B00111110, B00010100, // #
4, 8, B00100100, B01101010, B00101011, B00010010, B00000000, // $
5, 8, B01100011, B00010011, B00001000, B01100100, B01100011, // %
5, 8, B00110110, B01001001, B01010110, B00100000, B01010000, // &
1, 8, B00000011, B00000000, B00000000, B00000000, B00000000, // '
3, 8, B00011100, B00100010, B01000001, B00000000, B00000000, // (
3, 8, B01000001, B00100010, B00011100, B00000000, B00000000, // )
5, 8, B00101000, B00011000, B00001110, B00011000, B00101000, // *
5, 8, B00001000, B00001000, B00111110, B00001000, B00001000, // +
2, 8, B10110000, B01110000, B00000000, B00000000, B00000000, // ,
4, 8, B00001000, B00001000, B00001000, B00001000, B00000000, // -
2, 8, B01100000, B01100000, B00000000, B00000000, B00000000, // .
4, 8, B01100000, B00011000, B00000110, B00000001, B00000000, // /
4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // 0
3, 8, B01000010, B01111111, B01000000, B00000000, B00000000, // 1
4, 8, B01100010, B01010001, B01001001, B01000110, B00000000, // 2
4, 8, B00100010, B01000001, B01001001, B00110110, B00000000, // 3
4, 8, B00011000, B00010100, B00010010, B01111111, B00000000, // 4
4, 8, B00100111, B01000101, B01000101, B00111001, B00000000, // 5
4, 8, B00111110, B01001001, B01001001, B00110000, B00000000, // 6
4, 8, B01100001, B00010001, B00001001, B00000111, B00000000, // 7
4, 8, B00110110, B01001001, B01001001, B00110110, B00000000, // 8
4, 8, B00000110, B01001001, B01001001, B00111110, B00000000, // 9
2, 8, B01010000, B00000000, B00000000, B00000000, B00000000, // :
2, 8, B10000000, B01010000, B00000000, B00000000, B00000000, // ;
3, 8, B00010000, B00101000, B01000100, B00000000, B00000000, // <
3, 8, B00010100, B00010100, B00010100, B00000000, B00000000, // =
3, 8, B01000100, B00101000, B00010000, B00000000, B00000000, // >
4, 8, B00000010, B01011001, B00001001, B00000110, B00000000, // ?
5, 8, B00111110, B01001001, B01010101, B01011001, B00111110, // @
4, 8, B01111110, B00010001, B00010001, B01111110, B00000000, // A
4, 8, B01111111, B01001001, B01001001, B00110110, B00000000, // B
4, 8, B00111110, B01000001, B01000001, B00100010, B00000000, // C
4, 8, B01111111, B01000001, B01000001, B00111110, B00000000, // D
4, 8, B01111111, B01001001, B01001001, B01000001, B00000000, // E
4, 8, B01111111, B00001001, B00001001, B00000001, B00000000, // F
4, 8, B00111110, B01000001, B01001001, B01111010, B00000000, // G
4, 8, B01111111, B00001000, B00001000, B01111111, B00000000, // H
3, 8, B01000001, B01111111, B01000001, B00000000, B00000000, // I
4, 8, B00110000, B01000000, B01000001, B00111111, B00000000, // J
4, 8, B01111111, B00001000, B00010100, B01100011, B00000000, // K
4, 8, B01111111, B01000000, B01000000, B01000000, B00000000, // L
5, 8, B01111111, B00000010, B00001100, B00000010, B01111111, // M
5, 8, B01111111, B00000100, B00001000, B00010000, B01111111, // N
4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // O
4, 8, B01111111, B00001001, B00001001, B00000110, B00000000, // P
4, 8, B00111110, B01000001, B01000001, B10111110, B00000000, // Q
4, 8, B01111111, B00001001, B00001001, B01110110, B00000000, // R
4, 8, B00100110, B01001001, B01001001, B00110010, B00000000, // S
5, 8, B00000001, B00000001, B01111111, B00000001, B00000001, // T
4, 8, B00111111, B01000000, B01000000, B00111111, B00000000, // U
5, 8, B00001111, B00110000, B01000000, B00110000, B00001111, // V
5, 8, B00111111, B01000000, B00111000, B01000000, B00111111, // W
5, 8, B01100011, B00010100, B00001000, B00010100, B01100011, // X
5, 8, B00000111, B00001000, B01110000, B00001000, B00000111, // Y
4, 8, B01100001, B01010001, B01001001, B01000111, B00000000, // Z
2, 8, B01111111, B01000001, B00000000, B00000000, B00000000, // [
4, 8, B00000001, B00000110, B00011000, B01100000, B00000000, // \ backslash
2, 8, B01000001, B01111111, B00000000, B00000000, B00000000, // ]
3, 8, B00000010, B00000001, B00000010, B00000000, B00000000, // hat
4, 8, B01000000, B01000000, B01000000, B01000000, B00000000, // _
2, 8, B00000001, B00000010, B00000000, B00000000, B00000000, // `
4, 8, B00100000, B01010100, B01010100, B01111000, B00000000, // a
4, 8, B01111111, B01000100, B01000100, B00111000, B00000000, // b
4, 8, B00111000, B01000100, B01000100, B00101000, B00000000, // c
4, 8, B00111000, B01000100, B01000100, B01111111, B00000000, // d
4, 8, B00111000, B01010100, B01010100, B00011000, B00000000, // e
3, 8, B00000100, B01111110, B00000101, B00000000, B00000000, // f
4, 8, B10011000, B10100100, B10100100, B01111000, B00000000, // g
4, 8, B01111111, B00000100, B00000100, B01111000, B00000000, // h
3, 8, B01000100, B01111101, B01000000, B00000000, B00000000, // i
4, 8, B01000000, B10000000, B10000100, B01111101, B00000000, // j
4, 8, B01111111, B00010000, B00101000, B01000100, B00000000, // k
3, 8, B01000001, B01111111, B01000000, B00000000, B00000000, // l
5, 8, B01111100, B00000100, B01111100, B00000100, B01111000, // m
4, 8, B01111100, B00000100, B00000100, B01111000, B00000000, // n
4, 8, B00111000, B01000100, B01000100, B00111000, B00000000, // o
4, 8, B11111100, B00100100, B00100100, B00011000, B00000000, // p
4, 8, B00011000, B00100100, B00100100, B11111100, B00000000, // q
4, 8, B01111100, B00001000, B00000100, B00000100, B00000000, // r
4, 8, B01001000, B01010100, B01010100, B00100100, B00000000, // s
3, 8, B00000100, B00111111, B01000100, B00000000, B00000000, // t
4, 8, B00111100, B01000000, B01000000, B01111100, B00000000, // u
5, 8, B00011100, B00100000, B01000000, B00100000, B00011100, // v
5, 8, B00111100, B01000000, B00111100, B01000000, B00111100, // w
5, 8, B01000100, B00101000, B00010000, B00101000, B01000100, // x
4, 8, B10011100, B10100000, B10100000, B01111100, B00000000, // y
3, 8, B01100100, B01010100, B01001100, B00000000, B00000000, // z
3, 8, B00001000, B00110110, B01000001, B00000000, B00000000, // {
1, 8, B01111111, B00000000, B00000000, B00000000, B00000000, // |
3, 8, B01000001, B00110110, B00001000, B00000000, B00000000, // }
4, 8, B00001000, B00000100, B00001000, B00000100, B00000000, // ~
4, 8, B00111110, B01000001, B01000001, B00100010, B00010000, // Ç
};
int DIN = 12;
int CS = 11;
int CLK = 10;
int maxInUse = 1;
int Velocity = 60;
int Bright = 8;
byte buffer[10];
char Texto[64];
MaxMatrix m(DIN, CS, CLK, maxInUse);
void setup() {
m.init();
m.setIntensity(Bright);
Serial.begin(9600);
}
void loop() {
int i = 0;
int j = 0;
delay(500);
if (Serial.available()) {
while (Serial.available() > 0) {
Texto[i] = Serial.read();
i++;
}
Texto[i] = '\0';
}
PrintData(Texto);
Serial.println("Done");
}
void PrintData(char DATA[])
{
int i = 0;
int j = 0;
int X = 0;
int Z = 0;
String Texto(DATA);
if (Texto.startsWith(">SPD")) {
Velocity = Texto.substring(4).toInt();
for (j = 0; j < 11; j++) {
DATA[j] = 0;
}
i = 0;
}
if (Texto.startsWith(">LUM")) {
Bright = Texto.substring(4).toInt();
m.setIntensity(Bright);
for (j = 0; j < 11; j++) {
DATA[j] = 0;
}
i = 0;
}
if (Texto.startsWith(">DEMO")) {
for (j = 0; j < 11; j++) {
DATA[j] = 0;
}
i = 0;
}
else {
printStringWithShift(DATA, Velocity);
for (j = 0; j < 11; j++) {
DATA[j] = 0;
}
digitalWrite(13, LOW);
i = 0;
}
}
void printCharWithShift(char c, int shift_speed) {
if (c < 32) return;
c -= 32;
memcpy_P(buffer, CH + 7 * c, 7);
m.writeSprite(maxInUse * 8, 0, buffer);
m.setColumn(maxInUse * 8 + buffer[0], 0);
for (int i = 0; i < buffer[0] + 1; i++)
{
delay(shift_speed);
m.shiftLeft(false, false);
}
}
void printString(char* s) {
int col = 0;
while (*s != 0)
{
if (*s < 32) continue;
char c = *s - 32;
memcpy_P(buffer, CH + 7 * c, 7);
m.writeSprite(col, 0, buffer);
m.setColumn(col + buffer[0], 0);
col += buffer[0] + 1;
s++;
}
}
void printStringWithShift(char *s, int shift_speed) {
while (*s != 0) {
printCharWithShift(*s, shift_speed);
s++;
}
}
Poniendo todo junto.
Una vez que esta compilado y subido el código al arduino, es momento de poner todo junto. Para eso abriremos el programa que hicimos en C#, cuando lo abramos, ITunes también se abrirá
Primero debemos de escoger el puerto en el cual esta conectado el Arduino, en mi caso el puerto es "COM3", la velocidad de la conexión en Baudios, debe de ser la misma que pusimos al momento de iniciar la conexión serial en el código para el arduino, en nuestro caso usamos 9600.
![]() |
| Cómo luce la configuración en mi caso. |
Lo mas adecuado es conectar primero el arduino al PC y después iniciar la aplicación, de lo contrario no aparecerá en la lista, pero aun puedes escribir el puerto, por ejemplo: si tu arduino esta conectado al puerto "COM4", pero en la lista no aparece bastara con que escribas COM4
![]() |
| No se ve el puerto "COMX" solo escribelo. |
Y listo, ahora te ahorraras el trabajo de andar buscando la ventana de ITunes para ver que es lo que estás escuchando.
Parecido mas no igual.
Al ver el código de seguro dirás: ¿Oye, eso no lo he visto ya?, probablemente el mayor codigo del arduino es similar para mostrar el efecto scroll en la matriz, pero ademas de eso le he agregado funciones para poder cambiar el brillo de la matriz sin tener que cambiar el código en el arduino cada vez, lo mismo para la velocidad del texto, generalmente los ejemplos que vienen de otros sitios, la velocidad del texto y el brillo de la matriz es fijo, si quieres cambiarlo debes de conectar el arduino a su IDE, cambiar el código, subirlo y luego usarlo, en este caso evitamos eso creando funciones para ahorrarnos eso, aquí solo basta un clic y listo, el brillo está cambiado.
Por ahora es todo, si tienes dudas no dudes en preguntar. Si quieres ver todo el proceso, aquí está el primer paso.
Los leo luego.
Por ahora es todo, si tienes dudas no dudes en preguntar. Si quieres ver todo el proceso, aquí está el primer paso.
Los leo luego.
7/14/2016 01:21:00 p.m.
aprende
,
Arduino
,
csharp
,
DIY
,
electronica basica
,
how to
,
LED
,
Matiz LED
,
max7219
,
programacion
Vamos a programar #7 - Inútil-apps #1 - C# y arduino.
Avanzando lo anterior.
Hola de nuevo a todos el dia de hoy vamos a terminar de implementar lo que estabamos haciendo el post anterior.
Vamos a ver el código que hace funcionar a todo, está compuesto por dos partes, una de ellas es un programa para windows que se encargara de obtener la informacion de lo que estamos escuchando en ITunes.
De seguro te preguntarás: ¿Por qué Itunes y no otro programa? La respuesta es simple, ITunes ofrece soporte para que aquellos programadores que quieran hacaer uso del API de ITunes puedan hacerlo de manera sencilla, además de que el API tambien funciona en la plataforma .NET de microsoft.
La parte del PC. La aplicación para Windows.
La parte del PC. La aplicación para Windows.
Una vez que creamos en proyecto en #Develop o en C# de Visual studio. Lo primero que vamos a hacer es agregar una referencia a ITunes X.XX Type Library. Para eso iremos al menu Proyecto>Agregar Referencia.
![]() |
| La versión puede variar un poco dependiendo de la versión de itunes que se usa. |
La aplicación esta compuesta por lo siguientes controles (además del formulario):
- El formulario
- (name): MainForm
- Un GroupBox
- 4 Label (Los nombres no son necesarios)
- 1 ComboBox
- (name): cboportname
- 1 TextBox
- (name): txtBaudNumber
- Text: 9600
- 1 TrackBar
- (name): TrB
- Maximum: 15
- Minimum: 1
- 1 NumericUpDown
- (name): NUDVelocity
- Maximum: 500
- Minimum: 20
- Button
- (name):BtnConnect
- Text: Conectar
- Button
- (name):BtnDisconnect
- Text: Desconectar
/*
* MPM Develop
* Usuario: Ra
* Fecha: 13/07/2016
* Forget Your troubles C'mon get happy
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO.Ports;
using System.Windows.Forms;
using iTunesLib;
using System.Configuration;
namespace Message_Sender
{
public partial class MainForm : Form
{
int MessageType = 0;
private enum Trackinfo
{
Title = 0,
Artist,
Album,
BPM,
Duration
}
private SerialPort Port = new SerialPort();
private string GetTrackOnItunes(Trackinfo Detail)
{
iTunesApp ItunesInfo = new iTunesAppClass();
switch (ItunesInfo.PlayerState) {
case ITPlayerState.ITPlayerStateStopped:
return "Detenido ";
case ITPlayerState.ITPlayerStatePlaying:
IITTrack Track = ItunesInfo.CurrentTrack;
switch (Detail)
{
case MainForm.Trackinfo.Title:
return "Cancion: " + Track.Name + " ";
case MainForm.Trackinfo.Artist:
return "Artista: " + Track.Artist+ " ";
case MainForm.Trackinfo.Album:
return "Album: " + Track.Album+ " ";
case MainForm.Trackinfo.BPM:
return "BPM: " + Track.BPM.ToString()+ " ";
case MainForm.Trackinfo.Duration:
return "Duracion: " + TimeSpan.FromSeconds(Track.Duration).ToString()+ " ";
default:
return "??? ??? ??? ??? ";
}
case ITPlayerState.ITPlayerStateFastForward:
return ">> Avance Rapido >> ";
case ITPlayerState.ITPlayerStateRewind:
return "<< Retroceso Rapido << ";
default:
return "??? ??? ??? ??? ";
}
}
private void ArduinoMessage(string Message)
{
try
{
Port.Write(Message);
} catch (Exception e) {
MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
public MainForm()
{
InitializeComponent();
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
if (indata == "Done")
{
MessageType++;
if (MessageType > 4)
{
MessageType = 0;
}
ArduinoMessage(GetTrackOnItunes((Trackinfo)MessageType) + " ");
}
}
void MainFormLoad(object sender, EventArgs e)
{
string[] Ports = SerialPort.GetPortNames();
cboportname.Items.AddRange(Ports);
Port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
void BtnConnectClick(object sender, EventArgs e)
{
try {
if (Port.IsOpen == false)
{
Port.BaudRate = int.Parse(txtBaudNumber.Text);
Port.PortName = cboportname.Text;
Port.Open();
}
} catch (Exception ex) {
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
void BtnDisconnectClick(object sender, EventArgs e)
{
if (Port.IsOpen == true)
{
Port.Write("Se ha desconectado el cliente");
Port.Close();
}
}
void MainFormFormClosing(object sender, FormClosingEventArgs e)
{
if (Port.IsOpen == true)
{
Port.Close();
}
}
void TrBMouseUp(object sender, MouseEventArgs e)
{
ArduinoMessage(">LUM"+TrB.Value.ToString());
}
void NUDVelocityValueChanged(object sender, EventArgs e)
{
ArduinoMessage(">SPD"+NUDVelocity.Value.ToString());
}
}
}
La funcion del codigo es muy sencilla, lo unico que haces es primer crear un objeto del tipo Port y le asignamos un puerto (en mi caso COM3) con la velocidad de transferencia en 9600 Baudio. Después obtiene la informacion desde ITunes y la envía para el arduino.
Ahora solo nos resta por hacer el código para el arduino, pero eso será en la próxima ocasion.
Todo el código fuente lo puedes descargar aquí
Los leo luego
La siguiente parte está aquí.
Todo el código fuente lo puedes descargar aquí
Los leo luego
La siguiente parte está aquí.
7/13/2016 07:37:00 p.m.
aprende
,
Arduino
,
csharp
,
DIY
,
electronica basica
,
max7219
,
programacion
Suscribirse a:
Entradas
(
Atom
)
























