Mostrando las entradas con la etiqueta XVI32. Mostrar todas las entradas
Mostrando las entradas con la etiqueta XVI32. Mostrar todas las entradas
Vamos a programar #24 - Haciendo un instalador en NSIS #3 - Datos útiles.
EL día de hoy vamos a continuar con la creación de instalador en NSIS. En post anteriores, ya hemos visto alguna cosas útiles para incluirlas en el instalador. Para continuar y hacer un instalador lo más presentable posible, veremos algunas cosas más que ayudaran a crear un buen instalador.
Para agregar una entrada del tipo REG_SZ o DWORD usando NSIS usaremos las siguientes instrucciones:
La instrucción WriteRegStr es las que se usará cuando se quieran escribir valores REG_SZ (normalmente cadenas de texto) y la instrucción WriteRegDWORD, escribirá los valores DWORD (números por lo general).
Ahora supongamos que queremos agregar un comentario al instalador que usamos cómo ejemplo (antes de continuar, si no has visto el código fuente del instalador de VEncoder 2 sugiero que lo descargues y le eches un vistazo). Para agregar los comentarios, necesitamos escribir un valor en el registro, retomado el código anterior , solo hay que usar la función que escribe un valor REG_SZ y escribir un valor llamado "Comments" y su contenido será el texto que queremos mostrar.
Tras re-compilar el instalador y ejecutarlo, al buscar la información en el registro lucirá cómo se ve en la siguiente imagen:
Retomando el ejemplo que se incluye en la descarga, al compilarlo y ejecutarlo, verás que todos los archivos con extensión VEPX, cambian y en lugar de aparecer el icono de archivo desconocido, aparece uno como el de la imagen anterior. Si hacemos doble clic en el archivo, se abrirá un cuadro de dialogo preguntándonos que programa queremos usar para abrir ese tipo de archivos.
Normalmente, queremos que cuando el usuario haga clic en ellos, se abra nuestro programa y a partir de ahi, trabajaremos con este archivo.
Para poder abrir directamente el archivo con nuestro programa, debemos de crear entradas en el registro de Windows.
El código anterior muestra cómo se deben de crear las entradas en el registro de windows.
Para eso vamos a agregar el siguiente código en NSIS:
Tip #2 - Apéndice de todos los valores posibles para el des-instalador.
En el post anterior vimos que hay valores que agregandolos al registro de windows, mostraran información adicional, incluirlas o no ya es cuestión de cada quien, pero yo prefiero agregar al menos las mas primordiales, esto puede ayudar a que el usuario en determinado momento opte o no por quitar nuestra aplicación.
En la siguiente tabla se muestran todas las entradas posibles y que función cumplen, además de se muestra que tipo de entrada es.
En la siguiente tabla se muestran todas las entradas posibles y que función cumplen, además de se muestra que tipo de entrada es.
| Propiedad | Informacion | Tipo de entrada |
|---|---|---|
| AuthorizedCDFPrefix | La direccion URL para actualizar la aplicacion | REG_SZ |
| Comments | Comentarios adicionales para mostrarse | REG_SZ |
| DisplayIcon | El icono que se mostrara para representar a la aplicacion. | REG_SZ |
| DisplayName | El nombre del programa a mostrarse | REG_SZ |
| DisplayVersion | La version instalada del programa | REG_SZ |
| EstimatedSize | Tamaño estimado del programa (en KB) | DWORD |
| HelpLink | Direccion URL para el soporte | REG_SZ |
| HelpTelephone | Numero telefonico para ofrecer soporte | REG_SZ |
| InstallDate | Fecha de instalacion | REG_SZ |
| InstallLocation | Ubicacion del programa instalado | REG_SZ |
| InstallSource | Ubicacion de la fuente de instalacion | REG_SZ |
| Language | Idioma del programa | REG_SZ |
| ModifyPath | La ruta del programa de re-instalacion, reparacion | REG_SZ |
| NoModify | NO muestra la opcion de "Modificar". | DWORD |
| NoRepair | NO muestra la opcion de "Reparar" | DWORD |
| Publisher | Nombre del publicador | REG_SZ |
| Readme | La ubicacion del archivo "LEAME" | REG_SZ |
| UninstallString | La ruta del des-instalador | REG_SZ |
| URLInfoAbout | La direccion URL con informacion del programa | REG_SZ |
| URLUpdateInfo | La direccion URL con informacion de las actualizaciones | REG_SZ |
| Version | La version del programa (X.xx.xx) | REG_SZ |
| VersionMajor | La version Major del programa (x.XX.xx) | REG_SZ |
| VersionMinor | La version Minor del programa (x.xx.XX) | REG_SZ |
Para agregar una entrada del tipo REG_SZ o DWORD usando NSIS usaremos las siguientes instrucciones:
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "HelpLink" "http://xworkforall.blogspot.com/2016/07/el-video-correcto-2.html" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "NoModify" 1
La instrucción WriteRegStr es las que se usará cuando se quieran escribir valores REG_SZ (normalmente cadenas de texto) y la instrucción WriteRegDWORD, escribirá los valores DWORD (números por lo general).
Ahora supongamos que queremos agregar un comentario al instalador que usamos cómo ejemplo (antes de continuar, si no has visto el código fuente del instalador de VEncoder 2 sugiero que lo descargues y le eches un vistazo). Para agregar los comentarios, necesitamos escribir un valor en el registro, retomado el código anterior , solo hay que usar la función que escribe un valor REG_SZ y escribir un valor llamado "Comments" y su contenido será el texto que queremos mostrar.
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "HelpLink" "http://xworkforall.blogspot.com/2016/07/el-video-correcto-2.html" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "NoModify" 1 WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "Comments" "Eres libre se usarlo y modificarloa tu gusto, pero no reclamarlo cómo tuyo"
Tras re-compilar el instalador y ejecutarlo, al buscar la información en el registro lucirá cómo se ve en la siguiente imagen:
Tip #3 - Asociando una extensión a nuestro programa.
Otra parte de nuestro instalador, es la posibilidad de que ciertos tipos de archivo se abran con nuestro programa. NSIS ayuda a hacer esta tarea de forma sencilla.Normalmente, queremos que cuando el usuario haga clic en ellos, se abra nuestro programa y a partir de ahi, trabajaremos con este archivo.
Para poder abrir directamente el archivo con nuestro programa, debemos de crear entradas en el registro de Windows.
HKEY_CLASSES_ROOT MyProgram.exe shell open command (Default) = C:\MyDir\MyProgram.exe /a "%1" print command (Default) = C:\MyDir\MyProgram.exe /a /p "%1" printto command (Default) = C:\MyDir\MyProgram.exe /a /p "%1" "%2"
El código anterior muestra cómo se deben de crear las entradas en el registro de windows.
Para eso vamos a agregar el siguiente código en NSIS:
WriteRegStr HKCR ".VEPX" "" "VEncoder.VEPX" WriteRegStr HKCR "VEncoder.VEPX" "" "Archivo de perfil para Vencoder" WriteRegStr HKCR "Vencoder.VEPX\DefaultIcon" "" "$INSTDIR\VEPX.ico" WriteRegStr HKCR "VEncoder.VEPX\shell\Open\" "" "Cargar en VEncoder 2" WriteRegStr HKCR "VEncoder.VEPX\shell\Open\command" "" "$INSTDIR\Vencoder 2.exe"
Lo que hace el código anterior es lo siguiente (hay que recordar que todas las claves se crean en HKey Classes Root, HKCR):
- Creamos la entrada a la extensión ".VEPX", y en el valor predeterminado, escribimos que toda la información referente a esta extensión, estará en la clave "VEncoder.VEPX".
- Luego creamos la clave "Vencoder.VEPX" y al valor por default le asignamos el texto que describe nuestro archivo, en este caso usamos "Archivo de perfil para VEncoder"
- Luego asignamos el icono que queremos usar para representar a nuestros archivos. Debemos de crear una clave llamada "DefaultIcon" y al valor por default, le asignamos la ruta en donde se encuentra nuestro icono, archivo dll o exe que lo contenga; si se usa un archivo dll o exe, hay que indicar el indice (wscript.exe 4 por ejemplo).
- Para agregar items en el menú contextual de Windows, dentro de la clave "VEncoder.VEPX" y en general para cualquier programa que hayamos hecho, hay quie agregar una clave llamada "Shell". Para que la opción de abrir con nuestro programa sea la primera, debemos de agregar una sub-clave llamada "Open" y al valor por default, agregar el texto que queremos mostrar en el menú contextual.
- Para que la entrada anterior funcione, debemos de asignar que "cosa" debe de pasar cuando hagamos clic en el. Para el caso concreto de VEncoder, solo abriremos la aplicación, por lo que creamos una nueva sub-clave llamada "command" y en el valor debemos de asignar el programa junto con los parámetros para su ejecución, retomando la estructura anterior al código, ahí se muestran como se deben de pasar los argumentos y el nombre de archivo, sera representado por "%1". Si VEncoder aceptara parámetros (cosa que no es así por el momento), la instrucción debería de quedar similar a la siguiente: '$INSTDIR\Vencoder 2.exe "%1"', usaríamos primero las comillas simples y después las comillas dobles.
El resultado del código anterior se verá cómo este:
Para agregar entradas adicionales, solo debemos de repetir los últimos dos paso, solo cambiando el nombre de la clave:
Y el resultado será como el siguiente:
Para agregar entradas adicionales, solo debemos de repetir los últimos dos paso, solo cambiando el nombre de la clave:
WriteRegStr HKCR ".VEPX" "" "VEncoder.VEPX" WriteRegStr HKCR "VEncoder.VEPX" "" "Archivo de perfil para Vencoder" WriteRegStr HKCR "Vencoder.VEPX\DefaultIcon" "" "$INSTDIR\VEPX.ico" WriteRegStr HKCR "VEncoder.VEPX\shell\Open\" "" "Cargar en VEncoder 2" WriteRegStr HKCR "VEncoder.VEPX\shell\Open\command" "" "$INSTDIR\Vencoder 2.exe" WriteRegStr HKCR "VEncoder.VEPX\shell\Editar\" "" "Esta es una prueba" WriteRegStr HKCR "VEncoder.VEPX\shell\Editar\command" "" "$INSTDIR\Vencoder 2.exe"
Y el resultado será como el siguiente:
Igual que en los post anteriores, el código competo lo puedes descargar de aqui.
Y bien, por ahora es todo, dudas o comentarios, puedes ponerlos en la parte de abajo.
Los leo luego.
12/01/2016 04:21:00 p.m.
Convertir videos
,
DIY
,
ffmpeg
,
GUI
,
how to
,
Instalador
,
NSI
,
NSIS
,
programacion
,
Vencoder
,
video
,
XVI32
Vamos a programar #19 - Extraer una imagen embebida en un MP3 - ArtView
El día de hoy vamos a terminar (ahora si no es broma) el código para extraer las imágenes que se encuentran embebidas en los archivos MP3.
En el post anterior implementamos algunas partes del código y lo pasamos a funciones, antes de avanzar hay algo que quiero decir de una de las funciones anteriores. Una persona que visitó el blog me dijo que la función DataToAlbum no funcionaba bien, a pesar de que yo mostré imágenes en la que parecía que todo funcionaba bien, pero no era asi, al analizar todos los bytes que componían al string (solo la versión unicode), había un carácter no visible. El carácter es 0xFF, eso se debia a que también le pasábamos a la función GetString los bytes adicionales que se usan para indicar que se trata de un string unicote, para agregarlo, solo hay que movernos hasta el tercer byte. Recordemos que la función que hicimos recibía cómo parámetro una matriz de bytes donde el primero, era el byte que nos indicaba cual era el tipo de codificación; uno para unicode y cero para ASCII. En el caso de la codificacion ASCII no existía mayor problema porque seguido de ese byte, inmediatamente va lo que vendría siendo el string en sí, pero en el caso del string unicode, inmediatamente después del primer byte, siguen dos bytes que sirven para indicar que tipo de codificación se usa (en lectores de texto por ejemplo,0xFF 0xFE para unicode).
Para eso creamos la función BatchProcess cuyo código es el siguiente:
Está función recibe cómo parámetros una lista del tipo string que contiene todos los archivo de los cuales queremos extraerles la imágenes, el otro parámetro es un string que contiene la ruta (fólder) en donde guardáremos las imágenes. Después creamos un thread para no bloquear la interfaz principal. De hecho cuando pruebes el código, al agregar los archivos, si agregas muchos, notarás que la aplicación se congela un poco (dependiendo de cuantos agregues y de que tan potente sea tu equipo), esto lo deje así para que notes la diferenta entre usar un thread aparte y cuando no.
El uso del thread (hilo a partir de este momento). Para esta aplicación es realmente sencillo, ya que no hacemos uso de las funciones para controlarla como es debido, de hecho para saber si aun sigue ocupado ese hilo usamos el flag IsWorking. Para poder saber el progreso del proceso usamos un delegado que se encargara de recibir el valor actual del progreso, después llamará a la funcion UpdateProgress que se encargará de actualizar la propiedad Value del ProgressBar.
El código de la funcion UpdateProgress (incluido el delegado) es el siguiente:
Y ya solo que da el código que se encargará de hacer el trabajo. El uso de un flag para determinar si hay o no un proceso es importante, ya que podemos cerrar la ventana principal y algun proceso todavía se puede estar ejecutando, suponiendo que eres cómo yo y rara vez le pide a Windows que te dé permiso para sacar tu dispositivo, si lo extraes en medio de un proceso de lectura/escritura, puede ser que se dañe tu USB.
El código completo lo puedes descargar aquí, Dudas o comentarios no dudes en decirlo. Antes de terminar quiero agradecer al que reportó el error, preferiría que no usaran perfiles anónimos para poder dar el reconocimiento que se debe.
Por ahora es todo, los leo luego.
En el post anterior implementamos algunas partes del código y lo pasamos a funciones, antes de avanzar hay algo que quiero decir de una de las funciones anteriores. Una persona que visitó el blog me dijo que la función DataToAlbum no funcionaba bien, a pesar de que yo mostré imágenes en la que parecía que todo funcionaba bien, pero no era asi, al analizar todos los bytes que componían al string (solo la versión unicode), había un carácter no visible. El carácter es 0xFF, eso se debia a que también le pasábamos a la función GetString los bytes adicionales que se usan para indicar que se trata de un string unicote, para agregarlo, solo hay que movernos hasta el tercer byte. Recordemos que la función que hicimos recibía cómo parámetro una matriz de bytes donde el primero, era el byte que nos indicaba cual era el tipo de codificación; uno para unicode y cero para ASCII. En el caso de la codificacion ASCII no existía mayor problema porque seguido de ese byte, inmediatamente va lo que vendría siendo el string en sí, pero en el caso del string unicode, inmediatamente después del primer byte, siguen dos bytes que sirven para indicar que tipo de codificación se usa (en lectores de texto por ejemplo,0xFF 0xFE para unicode).
private string DataToAlbum(byte[] Data)
{
if (Data[0] == 0)
return Encoding.ASCII.GetString(Data,1,Data.Length-1).TrimEnd((char)0).TrimStart((char)0);
if (Data[0] == 1)
return Encoding.Unicode.GetString(Data,3,Data.Length-3).TrimEnd((char)0).TrimStart((char)0);
else
return "Desconocido";
}
Procesando por lotes.
El chiste de todo esto era extraer las imágenes debido a que cuando se convertían entre una versión a otra, por lo que me dijeron, algunos programas omiten la imagen, entonces lo ideal es extraer primero la imagen y después insertarla de nuevo, eso si, solo después de haber realizado la conversión a la versión de destino.Para eso creamos la función BatchProcess cuyo código es el siguiente:
private void BatchProccess(List<string> Files, string SaveFolder)
{
int Current = 0;
PBProgress.Maximum = Files.Count;
Thread DoWork = new Thread (new ThreadStart(() =>
{
foreach (string Item in Files)
{
ImageContent(Item,SaveFolder);
Current = Current + 1;
UpdateProgress(Current);
if (Current == Files.Count) {
IsWorking =false;
}
}
}
));
DoWork.Start();
}
Está función recibe cómo parámetros una lista del tipo string que contiene todos los archivo de los cuales queremos extraerles la imágenes, el otro parámetro es un string que contiene la ruta (fólder) en donde guardáremos las imágenes. Después creamos un thread para no bloquear la interfaz principal. De hecho cuando pruebes el código, al agregar los archivos, si agregas muchos, notarás que la aplicación se congela un poco (dependiendo de cuantos agregues y de que tan potente sea tu equipo), esto lo deje así para que notes la diferenta entre usar un thread aparte y cuando no.
El uso del thread (hilo a partir de este momento). Para esta aplicación es realmente sencillo, ya que no hacemos uso de las funciones para controlarla como es debido, de hecho para saber si aun sigue ocupado ese hilo usamos el flag IsWorking. Para poder saber el progreso del proceso usamos un delegado que se encargara de recibir el valor actual del progreso, después llamará a la funcion UpdateProgress que se encargará de actualizar la propiedad Value del ProgressBar.
El código de la funcion UpdateProgress (incluido el delegado) es el siguiente:
private delegate void UpdateProgressDelegate(int Value);
private void UpdateProgress(int Value)
{
if (PBProgress.InvokeRequired)
{
PBProgress.Invoke(new UpdateProgressDelegate(UpdateProgress), Value);
}
else
{
PBProgress.Value = Value;
}
}
El uso un poco más avanzado de los hilos lo veremos en el siguiente Post.Y ya solo que da el código que se encargará de hacer el trabajo. El uso de un flag para determinar si hay o no un proceso es importante, ya que podemos cerrar la ventana principal y algun proceso todavía se puede estar ejecutando, suponiendo que eres cómo yo y rara vez le pide a Windows que te dé permiso para sacar tu dispositivo, si lo extraes en medio de un proceso de lectura/escritura, puede ser que se dañe tu USB.
El código completo lo puedes descargar aquí, Dudas o comentarios no dudes en decirlo. Antes de terminar quiero agradecer al que reportó el error, preferiría que no usaran perfiles anónimos para poder dar el reconocimiento que se debe.
Por ahora es todo, los leo luego.
P.D, “Arceus existe
;)”
Vamos a programar #18 - Extraer una imagen embebida en un MP3.
Hola de nuevo a todos, el día de hoy vamos terminar con el programa encargado de extraer la imágenes que están embebidas en los archivos MP3.
El día de hoy vamos a implementar todo el programa y ademas haremos una pequeñas optimizaciones al código que ya teníamos previamente hecho.
Si recordamos en otro código, es exactamente lo mismo, solo que lo separamos para poder usarlo de manera rápida. Esta función recibe una matriz de bytes que contiene los 4 o 3 bytes después de la etiqueta "PIC" o "APIC". cómo resultado, devuelve un numero del tipo int que contiene la longitud de la etiqueta. Para usarla solo hay que sustituir la parte de los calculos por la llamada a la funcion.
Código anterior:
Código nuevo implementando la función ByteArrToSize:
y cómo es un cálculo que lo vamos a hacer más de una vez, lo mejor es usar la función, originalmente la hice de la primer forma debido a que quería mostrar lo que estábamos haciendo a mano, pero para fines más prácticos, usamos la función, no hace falta ser experto para ver que en ambos casos se hace exactamente lo mismo, solo que la función determina; hasta cierto punto; que es lo que va a hacer, si usáramos el código original, debido a que es diferente como esta descrito el tamaño de la etiqueta. Primero había que comprobar para que caso íbamos a hacer el cálculo, y de leer un byte, multiplicar y sumar 3 o 4veces dependiendo del caso. En la función solo pedimos los datos y dependiendo de el numero de bytes que contenga el argumento, seguirá las condiciones pertinentes para devolver el valor correcto.
Otro de los procesos que decidí volver en una función; fue el de obtener la imagen, si recordamos, en el código original, una vez que ya teníamos definidos todos los datos de la imagen (tipo y longitud), en cada una de las versiones de las versiones, leíamos los datos, creábamos un MemoryStream y creábamos una imagen a partir de el.
El codigo es el siguiente:
El siguiente código es la versión en función del primer código.
Al igual que en el caso anterior, tomamos todo el código que ya teníamos y lo usamos tal cual estaba, solo que este caso, devolvemos un valor del tipo Image que contiene la imagen que extrajimos. La función recibe dos parámetros: uno del tipo byte[] que es el que contiene todos los datos leídos previamente desde el Stream donde se manda a llamar esta función y un valor del tipo string que contiene la ruta en donde se guardará la imagen (en adición a la imagen que devuelve la función).
El día de hoy vamos a implementar todo el programa y ademas haremos una pequeñas optimizaciones al código que ya teníamos previamente hecho.
Mejorando el código previo.
Primero vamos crear una función para calcular el tamaño de la etiqueta, en el código, lo que hacíamos primero, era buscar la etiqueta, luego leer la longitud, pero hay que recordar que dependiendo de la versión, se usan tres o cuatro bytes para esto.
Entoces basados en la informacion anterior, podemos hacer una función similar a la siguiente:
private int ByteArrToSize(byte[] Data)
{
int Result = 0;
if (Data.Length == 4)
{
Result = Data[0] * 256 * 256 * 256 + Data[1] * 256 * 256 + Data[2] * 256 + Data[3];
return Result;
}
if (Data.Length == 3)
{
Result = Data[0] * 256 * 256 + Data[1] * 256 + Data[2];
return Result;
}
else return 0;
}
Si recordamos en otro código, es exactamente lo mismo, solo que lo separamos para poder usarlo de manera rápida. Esta función recibe una matriz de bytes que contiene los 4 o 3 bytes después de la etiqueta "PIC" o "APIC". cómo resultado, devuelve un numero del tipo int que contiene la longitud de la etiqueta. Para usarla solo hay que sustituir la parte de los calculos por la llamada a la funcion.
Código anterior:
//Size TotalLenght = TotalLenght + BinRead.ReadByte() * 256 * 256 * 256; TotalLenght = TotalLenght + BinRead.ReadByte() * 256 * 256; TotalLenght = TotalLenght + BinRead.ReadByte() * 256; TotalLenght = TotalLenght + BinRead.ReadByte(); txtInfo.Text = txtInfo.Text + "\n La etiqueta APIC esta en la dirección: " + PicTagPos.ToString() + "\n"; txtInfo.Text = txtInfo.Text + "con longitud de" + TotalLenght.ToString() + " bytes\n";
Código nuevo implementando la función ByteArrToSize:
TotalLenght = ByteArrToSize(BinRead.ReadBytes(4)); txtInfo.Text = txtInfo.Text + "\n La etiqueta APIC esta en la dirección: " + PicTagPos.ToString() + "\n"; txtInfo.Text = txtInfo.Text + "con longitud de" + TotalLenght.ToString() + " bytes\n";
y cómo es un cálculo que lo vamos a hacer más de una vez, lo mejor es usar la función, originalmente la hice de la primer forma debido a que quería mostrar lo que estábamos haciendo a mano, pero para fines más prácticos, usamos la función, no hace falta ser experto para ver que en ambos casos se hace exactamente lo mismo, solo que la función determina; hasta cierto punto; que es lo que va a hacer, si usáramos el código original, debido a que es diferente como esta descrito el tamaño de la etiqueta. Primero había que comprobar para que caso íbamos a hacer el cálculo, y de leer un byte, multiplicar y sumar 3 o 4veces dependiendo del caso. En la función solo pedimos los datos y dependiendo de el numero de bytes que contenga el argumento, seguirá las condiciones pertinentes para devolver el valor correcto.
Otro de los procesos que decidí volver en una función; fue el de obtener la imagen, si recordamos, en el código original, una vez que ya teníamos definidos todos los datos de la imagen (tipo y longitud), en cada una de las versiones de las versiones, leíamos los datos, creábamos un MemoryStream y creábamos una imagen a partir de el.
El codigo es el siguiente:
using (MemoryStream MS = new MemoryStream(BinRead.ReadBytes(ImageLenght)))
{
PicArt.Image = Image.FromStream(MS);
using (FileStream file = new FileStream(SaveLocation + "." + ImageType(MType), FileMode.Create, FileAccess.Write))
{
MS.WriteTo(file);
file.Flush();
file.Close();
}
MS.Close();
}
Entonces para pasar todo el fragmento de código anterior, solo necesitamos crear una función que devuelva una imagen cómo resultado.
El siguiente código es la versión en función del primer código.
private Image StreamToImage(byte[] Data, string SaveLocation)
{
using (MemoryStream MS = new MemoryStream(Data))
{
Image TempImage;
using (FileStream file = new FileStream(SaveLocation, FileMode.Create, FileAccess.Write))
{
MS.WriteTo(file);
file.Flush();
file.Close();
TempImage = Image.FromStream(MS);
}
MS.Close();
return TempImage;
}
}
Al igual que en el caso anterior, tomamos todo el código que ya teníamos y lo usamos tal cual estaba, solo que este caso, devolvemos un valor del tipo Image que contiene la imagen que extrajimos. La función recibe dos parámetros: uno del tipo byte[] que es el que contiene todos los datos leídos previamente desde el Stream donde se manda a llamar esta función y un valor del tipo string que contiene la ruta en donde se guardará la imagen (en adición a la imagen que devuelve la función).
El código nuevo.
Todos estos post comenzaron por el hecho de que cuando se realiza una conversión entre una versión de las etiquetas ID3 a otra, en muchos casos se pierde la imagen (o eso fue lo que me dijeron), entonces nuestro objetivo es crear un Back up de estás. Pero ahora que ya sabemos cómo extraerlas, sigue almacenarlas, pero creo que surgiría un pequeño problema.
Cuando guardábamos las imágenes, les poníamos como nombre de archivo el nombre de la canción, solo que en lugar de extensión MP3 le poníamos JPG o PNG (dependiendo el caso), pero imaginemos que tenemos el álbum completo, no tendría caso extraer la imagen para cada una, porque seria la misma para cada canción del mismo álbum, supongamos que tenemos un disco completo ya en MP3 y este contiene 20 canciones, si le extraemos la imagen a cada uno, tendríamos 20 archivos de imagen iguales con nombres distintos.
La solucion es un tanto sencilla, solo debemos de tomar cómo nombre de archivo algo que tengan en comun los archivos que no sea el artista. El album es un buen identificador, porque bastará que extraigamos una sola imagen para cubrir los 20 archivos.
Para poder usar el nombre del album cómo nombre de archivo para la imagen, primero debemos de saber cual es el nombre del album. Igual que con la imagen, dentro del tag ID3 hay un frame que describe la información para el album. En el caso de la version 2.3 es la siguiente:
La solucion es un tanto sencilla, solo debemos de tomar cómo nombre de archivo algo que tengan en comun los archivos que no sea el artista. El album es un buen identificador, porque bastará que extraigamos una sola imagen para cubrir los 20 archivos.
Para poder usar el nombre del album cómo nombre de archivo para la imagen, primero debemos de saber cual es el nombre del album. Igual que con la imagen, dentro del tag ID3 hay un frame que describe la información para el album. En el caso de la version 2.3 es la siguiente:
Text information frames The text information frames are the most important frames, containing information like artist, album and more. There may only be one text information frame of its kind in an tag. If the textstring is followed by a termination ($00 (00)) all the following information should be ignored and not be displayed. All text frame identifiers begin with "T". Only text frame identifiers begin with "T", with the exception of the "TXXX" frame. All the text information frames have the following format:Y para la version 2 es la siguiente:
<Header for 'Text information frame', ID: "T000" - "TZZZ", excluding "TXXX" described in 4.2.2.>
Text encoding $xx Information
text string according to encoding
The text information frames are the most important frames, containing information like artist, album and more. There may only be one text information frame of its kind in an tag. If the textstring is followed by a termination ($00 (00)) all the following information should be ignored and not be displayed. All the text information frames have the following format:Entonces sabiendo lo anterior podemos crear una código cómo el que sigue:
Text information identifier "T00" - "TZZ" , excluding "TXX", described in 4.2.2.
Frame size $xx xx xx
Text encoding $xx
Information <textstring>
private string DataToAlbum(byte[] Data)
{
if (Data[0] == 0)
return Encoding.ASCII.GetString(Data, 1, Data.Length - 1);
if (Data[0] == 1)
return Encoding.Unicode.GetString(Data, 1, Data.Length - 1);
else
return null;
}
El código anterior sirve para leer cualquier frame de texto (exceptuando los que dice la documentación), pero ademas incluye casos definidos para cuando el texto es ASCII o unicode. La función recibe como argumento una matriz de bytes que incluye todos los bytes que le siguen a la longitud del frame, es decir, el primer byte deberá de ser el que indica que tipo de codificación se va a usar; inmediatamente seguido de el string (que es el resto de la etiqueta)esto para el caso de la versión 2. Para la versión 2.3 se deben de pasar los bytes empezando desde el byte que indica la codificación del texto, omitimos los 2 bytes que se usan cómo flags.
La función se debe de mandar a llamar cuando ya tenemos separados los datos de este frame, es decir, primero hay que determinar que tipo de tag ID3 es, luego buscar "TALB" o "TAL" dependiendo de la versión y leer los datos de la misma forma que lo hicimos para el frame "APIC" o" PIC", esto se puede hacer dentro del mismo código que lee los datos para buscar la imagen y ya una vez que se encuentran, simplemente le pasamos la información a la función.
Es importante leer la codificación, porque para este caso, los caracteres del japones, no están disponibles en el estandar ASCII, Cuando el byte que nos indica que codificación se usa es 1, hay que recordar que la codificación es UNICODE, cuando es 0 es ASCII.
Y bien, por ahora solo queda pendiente la extracción por lotes, pero será en el siguiente post.
Los leo luego.
La función se debe de mandar a llamar cuando ya tenemos separados los datos de este frame, es decir, primero hay que determinar que tipo de tag ID3 es, luego buscar "TALB" o "TAL" dependiendo de la versión y leer los datos de la misma forma que lo hicimos para el frame "APIC" o" PIC", esto se puede hacer dentro del mismo código que lee los datos para buscar la imagen y ya una vez que se encuentran, simplemente le pasamos la información a la función.
![]() |
| Es importante usar la codificación correcta. Si no lo hiciéramos, no podríamos leer "ドリドリ" que es el álbum de esta canción. |
Es importante leer la codificación, porque para este caso, los caracteres del japones, no están disponibles en el estandar ASCII, Cuando el byte que nos indica que codificación se usa es 1, hay que recordar que la codificación es UNICODE, cuando es 0 es ASCII.
Y bien, por ahora solo queda pendiente la extracción por lotes, pero será en el siguiente post.
Los leo luego.
10/13/2016 06:55:00 p.m.
APIC
,
csharp
,
DIY
,
how to
,
interesante
,
mp3
,
programacion
,
XVI32
Vamos a programar #17 - Extraer una imagen embebida en un MP3
Hola a todos de nuevo. El día de hoy vamos a terminar con el temas de las imágenes embebidas en los archivos MP3.
En el post anterior, hicimos una parte del código de C# encargado de hacer la extracción. Había quedado pendiente el trabajo para la versión 2 de las etiquetas ID3.
Hay que recordar que la esctructura del la etiqueta "PIC" es la siguiente:
En el post anterior, hicimos una parte del código de C# encargado de hacer la extracción. Había quedado pendiente el trabajo para la versión 2 de las etiquetas ID3.
Hay que recordar que la esctructura del la etiqueta "PIC" es la siguiente:
Attached picture "PIC"
Frame size $xx xx xx
Text encoding $xx
Image format $xx xx xx
Picture type $xx
Description <textstring> $00 (00)
Picture data <binary data>
Basados en la información anterior crearemos el código necesario.
El código para la versión 2.
Retomando el codigo anterior, solo hay que agregar lo que nos hace falta. La función ImageContent constaba de 3 condiciones if importantes (ademas de algunos secundarios), el primero y principal consiste en verficar si hay una etiqueta ID3 valida, si la había, el siguiente if comprobaba que la version fuera la 2.3, el siguiente comprobaba para la version 2. Está fue la que quedo pendiente, el siguiente codigo va inmediatamente despues del if que comprobaba para la version 2.3.
if (MyTagType == TagVersion.TagVer22)
{
{
//PIC
BinRead.BaseStream.Position = PicTagPos + 3;
//Tamaño
TotalLenght = TotalLenght + BinRead.ReadByte() * 256 * 256;
TotalLenght = TotalLenght + BinRead.ReadByte() * 256;
TotalLenght = TotalLenght + BinRead.ReadByte();
txtInfo.Text = txtInfo.Text + "\n La etiqueta PIC esta en la dirección: " + PicTagPos.ToString() + "\n";
txtInfo.Text = txtInfo.Text + "con longitud de" + TotalLenght.ToString() + " bytes\n";
//TextEncoding
BinRead.ReadByte();
//Type JPG o PNG
string MType = "";
MType = MType + string.Concat((char)BinRead.ReadByte());
MType = MType + string.Concat((char)BinRead.ReadByte());
MType = MType + string.Concat((char)BinRead.ReadByte());
txtInfo.Text = txtInfo.Text + "\nDel tipo " + MType;
//Tipo de imagen
BinRead.ReadByte();
//Reciclamos el flag anterior
IsNotZero = true;
string ImgDescription = "";
//Descripcion de la imagen
while (IsNotZero == false)
{
byte Val = BinRead.ReadByte();
if (Val != 0x00)
{
ImgDescription = ImgDescription + string.Concat((char)Val);
IsNotZero = false;
}
else
{
IsNotZero = true;
}
}
long ImageFileBegin = BinRead.BaseStream.Position;
int ImageLenght = (TotalLenght - 1 - ((int)ImageFileBegin - PicTagPos - 6));
txtInfo.Text = txtInfo.Text + " La imagen empieza en " + ImageFileBegin.ToString();
txtInfo.Text = txtInfo.Text + "Y mide " + ImageLenght.ToString();
//Creamos una imagen desde el stream y la asignamos al picbox
//Ponemos el puntero al inicio de la imagen
BinRead.BaseStream.Position = ImageFileBegin + 1;
using (MemoryStream MS = new MemoryStream(BinRead.ReadBytes(ImageLenght)))
{
PicArt.Image = Image.FromStream(MS);
using (FileStream file = new FileStream(SaveLocation + "." + ImageType(MType), FileMode.Create, FileAccess.Write))
{
MS.WriteTo(file);
file.Flush();
file.Close();
}
MS.Close();
}
}
}
}
Si revisamos el código de forma detenida veremos que es mu similar al código anterior. Entonces te preguntarás ¿No es un desperdicio? Si y no, el código está pensado para hacer una demostración de lo que hicimos en el primer post? Y la respuesta es: NO, lo que trate de hacer con estos post es demostrar como es que se puede portar una situación al código así "tal cual", porque de hecho lo que hicimos aquí, fue lo mismo que hicimos con el programa XVI32. Es decir lo que primero hicimos a mano, luego lo plasmamos en el código.
En el próximo post crearemos una función para procesar por lotes y liberaré la aplicación completa, por ahora es todo.
Los leo luego.
![]() |
| Información para una canción con etiqueta PIC (Ver 2) |
![]() |
| Información para una canción con etiqueta APIC (Ver 2.3 {Lo admito soy un Pokefan!!!}) |
Los leo luego.
Vamos a programar #16 - Extraer una imagen embebida en un MP3
Hola de nuevo a todos, han pasado algunos días desde la ultima vez que hice un post y finalmente le traigo uno nuevo. Cómo recordaran, en el post anterior vimos como extraer una imagen embebida en un mp3, pero en esa ocasión lo hicimos manualmente. En este post vamos a hacer un programa en c# que se encargue de hacer de forma automática ese proceso.
El código en c#.
Primero que nada haremos un pequeño repaso a que es lo que vamos a hacer.
- Abrimos un archivo mp3.
- Leemos una fracción del archivo para determinar si existe o no la etiqueta "APIC" o "PIC".
- Si existe Busacmos su posicion.
- Leemos el tamaño total de la etiqueta.
- Leemos los datos adicionales (tipo, codificacion, etc).
- Leemos el archivo de la imagen.
- Guardamos el archivo de imagen.
- Listo.
Ahora agregaremos solo 5 controles:
- TextBox
- (name) = TxtPath
- Button
- (name) = BtnOpen
- Text = "Abrir"
- Button
- (name) = BtnExtract
- Text = Extraer
- TextBox
- (name) = TxtInfo
- ScrollBars = Vertical
- Multiline = True
- PictureBox
- (name) = PicArt
Basados en lo anterior, lo primero que debemos de hacer es identificar en que versión estamos trabajando, para eso crearemos una función que se encargue de devolver la versión. Para evitar que se usen valores desconocidos, en primer lugar crearemos una enumeración que contenga solo los valores posibles para nuestro proyecto.
private enum TagVersion
{
TagVer22,
TagVer23,
TagVerUnknow
}
En la enumeración anterior se definen situaciones para la versión 2 y 2.3, cualquier otra versión devolverá el valor "TagVerUnknow". Teniendo una definición para los valores que podemos esperar, ahora si creamos una función que devuelva uno de los valores de la enumeración.
La funcion GetId3Ver recibe cómo parámetro una matriz de bytes, después lee los primeros 3 valores y compra si corresponden a "ID3", si si lo son, después leen el cuarto byte y regresan el la versión de acuerdo al valor de este. hay que recordar que cuando hay una etiqueta ID3, los primero bytes describen la etiqueta en si y el cuarto byte nos dice que versión es; 2 para la versión 2, 3 para la versión 2.3 y 4 para la versión 2.4.
Ahora vamos a crear la función que se encargará de leer toda la información y guardarla en un archivo a parte.
Hacemos exactamente lo que hicimos cuando extrajimos la imagen a mano, primero leeremos el archivo, buscamos la etiqueta y luego leemos el archivo, determinamos el tipo de imagen que es, para eso usaremos la siguiente funcion:
Está función solo devuelve "PNG" o "JPG" y se usa al momento de guardar el resultado; es decir al momento de construir el nombre de archivo.
Finalmente solo queda mandar a llamar a la función "ImageContent" desde el botón BtnExtract, la función guardara la imagen con el mismo nombre de archivo que el del MP3 que usemos y también mostrará la imagen en el picture box "PicArt"
Ahora solo queda hacer todo el procedimiento para la versión 2, pero eso lo haremos en el siguiente Post, ademas crearemos una función para agregar muchos archivos y realizar la extracción por lotes. Te recomiendo que le eches un vistazo a la documentación parta que sea más fácil entender cómo es que funciona.
Por el momento es todo, los leo luego.
private TagVersion GetId3Ver(byte[] Data)
{//49 44 33
if (Data[0] == 0x49 && Data[1] == 0x44 && Data[2] == 0x33)
{
if (Data[3] == 2) return TagVersion.TagVer22;
if (Data[3] == 3) return TagVersion.TagVer23;
else return TagVersion.TagVerUnknow;
}
else
{
return TagVersion.TagVerUnknow;
}
}
La funcion GetId3Ver recibe cómo parámetro una matriz de bytes, después lee los primeros 3 valores y compra si corresponden a "ID3", si si lo son, después leen el cuarto byte y regresan el la versión de acuerdo al valor de este. hay que recordar que cuando hay una etiqueta ID3, los primero bytes describen la etiqueta en si y el cuarto byte nos dice que versión es; 2 para la versión 2, 3 para la versión 2.3 y 4 para la versión 2.4.
Ahora vamos a crear la función que se encargará de leer toda la información y guardarla en un archivo a parte.
private void ImageContent(string FilePath,string SaveLocation)
{
Byte[] BinSearchData;
Stream FS = new FileStream(FilePath, FileMode.Open, FileAccess.Read,FileShare.Read);
using(BinaryReader BinRead = new BinaryReader(FS,Encoding.Default))
{
BinSearchData = BinRead.ReadBytes(1024);
TagVersion MyTagType;
string MyTag;
switch (GetId3Ver(BinSearchData))
{
case TagVersion.TagVer22:
MyTag = "PIC";
MyTagType = TagVersion.TagVer22;
break;
case TagVersion.TagVer23:
MyTag = "APIC";
MyTagType = TagVersion.TagVer23;
break;
default:
MyTag = "Error";
MyTagType = TagVersion.TagVerUnknow;
break;
}
Match FindTag = Regex.Match(Encoding.ASCII.GetString(BinSearchData), MyTag);
if (FindTag.Success)
{
int TotalLenght = 0;
int PicTagPos = FindTag.Index;
if (MyTagType == TagVersion.TagVer23)
{
bool IsNotZero = false;
BinRead.BaseStream.Position = PicTagPos + 4;
//Size
TotalLenght = TotalLenght + BinRead.ReadByte() * 256 * 256 * 256;
TotalLenght = TotalLenght + BinRead.ReadByte() * 256 * 256;
TotalLenght = TotalLenght + BinRead.ReadByte() * 256;
TotalLenght = TotalLenght + BinRead.ReadByte();
txtInfo.Text = txtInfo.Text + "\n La etiqueta APIC esta en la dirección: " + PicTagPos.ToString() + "\n";
txtInfo.Text = txtInfo.Text + "con longitud de" + TotalLenght.ToString() + " bytes\n";
//Flags, por ahora no nos interesan
BinRead.ReadByte();
BinRead.ReadByte();
//TextEncoding
BinRead.ReadByte();
//Type
string MType= "";
while (IsNotZero == false)
{
byte Val = BinRead.ReadByte();
if (Val != 0x00)
{
IsNotZero = false;
MType = MType + string.Concat((char)Val);
}
else
{
IsNotZero = true;
}
}
txtInfo.Text = txtInfo.Text + "\nDel tipo " + MType;
//Tipo de imagen
BinRead.ReadByte();
//Reciclamos el flag anterior
IsNotZero = true;
string ImgDescription = "";
//Descripcion de la imagen
while (IsNotZero == false)
{
byte Val = BinRead.ReadByte();
if (Val != 0x00)
{
ImgDescription = ImgDescription + string.Concat((char)Val);
IsNotZero = false;
}
else
{
IsNotZero = true;
}
}
long ImageFileBegin = BinRead.BaseStream.Position;
int ImageLenght = (TotalLenght- 1 - ((int)ImageFileBegin - PicTagPos - 10));
txtInfo.Text = txtInfo.Text + " La imagen empieza en " + ImageFileBegin.ToString();
txtInfo.Text = txtInfo.Text + "Y mide " + ImageLenght.ToString();
//Creamos una imagen desde el stream y la asignamos al picbox
//Ponemos el puntero al inicio de la imagen
BinRead.BaseStream.Position = ImageFileBegin + 1;
using (MemoryStream MS = new MemoryStream(BinRead.ReadBytes(ImageLenght)))
{
PicArt.Image = Image.FromStream(MS);
using (FileStream file = new FileStream(SaveLocation + "." + ImageType(MType), FileMode.Create, FileAccess.Write))
{
MS.WriteTo(file);
file.Flush();
file.Close();
}
MS.Close();
}
}
if (MyTagType == TagVersion.TagVer22)
{
//Lo mismo que para la anterior, solo ajustando los datos, queda de tarea
}
}
else
{
ErrPr.SetError(BtnOpen, "No hay etiqueta APIC");
}
}
FS.Close();
}
Hacemos exactamente lo que hicimos cuando extrajimos la imagen a mano, primero leeremos el archivo, buscamos la etiqueta y luego leemos el archivo, determinamos el tipo de imagen que es, para eso usaremos la siguiente funcion:
private string ImageType(string MIMEType)
{
//PNG or JPG
//image/png or image/jpeg
if (MIMEType == "PNG" || MIMEType == "image/png")
return "png";
if (MIMEType == "JPG" || MIMEType == "image/jpeg")
return "jpg";
else
return "bin";
}
Está función solo devuelve "PNG" o "JPG" y se usa al momento de guardar el resultado; es decir al momento de construir el nombre de archivo.
Ahora solo queda hacer todo el procedimiento para la versión 2, pero eso lo haremos en el siguiente Post, ademas crearemos una función para agregar muchos archivos y realizar la extracción por lotes. Te recomiendo que le eches un vistazo a la documentación parta que sea más fácil entender cómo es que funciona.
Por el momento es todo, los leo luego.
Vamos a programar #15 - Extraer una imagen embebida en un MP3
Hace algunos días estaba hablando con un amigo y me comento un problema que le pasó. Hace algunos días quiso organizar su colección de música en formato MP3. Todas las canciones poseen los meta-datos o "TAGS" que sirven para identificar a una canción. La información básica que suele incluirse es:
- Nombre de la canción.
- Álbum.
- Artista.
- Año.
- Genero.
Etiquetas ID3.
Antes que nada veamos que es una etiqueta ID3.
ID3 es un estándar de facto para incluir meta-datos (etiquetas) en un archivo contenedor audiovisual, tales como álbum, título o artista. Se utiliza principalmente en ficheros sonoros como MP3. Wikipedia/ID3.Básicamente es eso. Para identificar cada una de las partes de la información se usan etiquetas ya predefinidas, es decir: si se quiere saber el autor se deberá de buscar una etiqueta en especifico.
La lista completa de etiquetas se puede encontrar en ID3/V2 (para la version 2.2.0) y en ID3V2.3(para la version 2.3).
Por ahora solo vamos a revisar la etiquetas que nos interesan:
Para la version 2.3
Header for 'Attached picture', ID: "APIC"
MIME type <text string> $00
Picture type $xx
Description <text string according to encoding> $00 (00)
Picture data <binary data>
Para la version 2.2Lo que haremos es buscar "PIC" o "APIC" para determinar si hay imagen o no, si la hay, después leeremos la longitud, luego el tipo (usualmente solo se usa PNG o JPG) y finalmente leeremos los datos correspondientes a la imagen en sí.
Attached picture "PIC"
Frame size $xx xx xx
Text encoding $xx
Image format $xx xx xx
Picture type $xx
Description <textstring> $00 (00)
Picture data <binary data>
Vista previa.
Antes que nada, primero vamos a hacer una pequeña prueba "en crudo" y trataremos de seguir las instrucciones a mano, es decir solo usando un lector hexadecimal, extraeremos la imagen. Para eso necesitarás un editor hexadecimal, hay muchos disponibles actualmente, pero yo uso XVI32, puedes usar el de tu preferencia, pero el programa que escojas deberá de tener las funciones de lectura y escritura.También recomiendo que leas las definiciones para las etiquetas antes de continuar.
Primero abriremos el archivo del cual queremos obtener la imagen. Una vez abierto procederemos a buscar directamente los datos que nos interesan. Para nuestro caso solo requerimos saber si hay una etiqueta "PIC" o "APIC". Entonces, con nuestro archivo ya abierto buscaremos eso.
Al terminar la búsqueda, si existe, el cursor se moverá a la posición en donde se encuentra el inicio del lo que buscamos.
Si hacemos memoria recordaremos que después de la etiqueta "APIC", los siguientes 4 bytes indican el tamaño total de la etiqueta y dos bytes más son usados cómo flags (no nos fijaremos en esto), entonces los datos que nos interesan son lo que estan en el recuadro rojo de la siguiente imagen:
Para este caso, la longitud de esta etiqueta es 0x00027225, hay que recordar que esta longitud no incluye los 10 bytes que se usan para definir la etiqueta, el tamaño y los flag. Si seleccionamos los 0x00027225 bytes, estaríamos seleccionando los datos que describen la imagen, el tipo de imagen (icono, cubierta frontal, etc) y formato de imagen (jpg o png).
Para poder guardar la imagen, seleccionaremos tantos bytes como nos indica, pero le restaremos 14 bytes que corresponden a los frames que describen para el tag APIC.
Si convertimos el numero 0x00027225 a decimal,obtenemos el numero decimal 160,293 restandole 14 obtenemos 160,279.
En nuestro editor primero pondremos el cursor al final de todos los descriptores de la etiqueta. y si tiene disponible la opcion de seleccionar una cantidad "n" de caracteres, usaremos esa indicando la cantidad de bytes que obtuvimos al hacer la resta.
Finalmente solo guardaremos la parte seleccionada (si el editor lo soporta).
Hay algo importante por remarcar, según la descripción en ID3.org es preferible usar solo dos tipos de imágenes, puede ser Jpeg o PNG, dependiendo de cual se use, el descriptor que se usa es diferente. En este ejemplo al ser una imagen jpg la que está incrustada en el mp3, al ver la descripción tenemos: "image/jpeg" pero si la imagen esta en png tendremos algo como: image/png. Eso causaría que si solo restamos a la longitud 14 bytes, la imagen no la obtendríamos completa, por eso es necesario revisar el tipo de imagen, ademas de que hay información adicional que en este caso no la hay.
Y bien es todo por hoy, en el siguiente post continuaremos ahora si con el código de C# para extraer la imágenes dentro de los mp3. Este post solo trata de exponer como es que funciona y de hecho ya hiciste a mano lo que C# haría internamente.
Si solo quieres seguir los pasos tal como están en el post. La canción que use como prueba está aquí.
Los leo luego.
Suscribirse a:
Entradas
(
Atom
)





















