2012-10-04 32 views
6

Estoy tratando de combinar 2 archivos AAC en uno, descubrí que en los archivos AAC, el elemento de encabezado está presente hasta el byte FF8, y luego los siguientes 4 bytes contienen la longitud de los datos de el AAC. Traté de mantener 1 matriz de encabezado, agregar el tamaño de los 2 archivos AAC y luego agregar el búfer de datos de los dos archivos uno después del otro.Combinar 2 archivos AAC usando Java

El archivo resultante solo reproduce el primer archivo AAC. Aquí está el fragmento de código.

FileInputStream fs = new FileInputStream("./res/after.aac"); 

dis = new DataInputStream(fs); 
headerData = new byte[0xFF8]; 
dis.read(headerData); 


int lengthTotal = dis.readInt(); 
System.out.println("Length of After == "+lengthTotal); 
dis.readInt(); 


data = new byte[dis.available()]; 

dis.readFully(data); 
dis.close(); 
dis = null; 
fs.close(); 
fs = null; 


fs = new FileInputStream("./res/continue.aac"); 
dis = new DataInputStream(fs); 

dis.skipBytes(0xFF8); 

int length = dis.readInt(); 
System.out.println("Length of Ahead == "+length); 
lengthTotal = lengthTotal + length -8; 
System.out.println("Total Length== "+lengthTotal); 
dis.readInt(); 
newData = new byte[dis.available()]; 
dis.read(newData); 

FileOutputStream fos = new FileOutputStream("./res/combine.aac"); 
DataOutputStream dos = new DataOutputStream(fos); 

dos.write(headerData); 
dos.writeInt(lengthTotal); 
dos.writeBytes("mdat"); 
dos.write(data); 
dos.write(newData); 

Yo sé que no hay información sobre la duración de tiempo del archivo AAC en el byte número 56, pero no estoy siendo capaz de entenderlo. ¿Alguien puede ayudarme aquí?

+0

Estoy intentando lo mismo ... ¿Lo arreglaron? ¿Alguna solución? –

Respuesta

1

Bueno, no puedo decirte lo que estás haciendo mal. Pero puedo decirte cómo hacer lo que quieres hacer.

En primer lugar crear un Ayudante General Funciones:

public static class General { 
    public static void CopyBytes(byte[] dst, int dstOffset, byte[] src) { 
     Buffer.BlockCopy(src, 0, dst, dstOffset, src.Length); 
    } 
} 

public static class BitHelper { 
    public static int Read(ref ulong x, int length) { 
     int r = (int)(x >> (64 - length)); 
     x <<= length; 
     return r; 
    } 

    public static int Read(byte[] bytes, ref int offset, int length) { 
     int startByte = offset/8; 
     int endByte = (offset + length - 1)/8; 
     int skipBits = offset % 8; 
     ulong bits = 0; 
     for (int i = 0; i <= Math.Min(endByte - startByte, 7); i++) { 
      bits |= (ulong)bytes[startByte + i] << (56 - (i * 8)); 
     } 
     if (skipBits != 0) Read(ref bits, skipBits); 
     offset += length; 
     return Read(ref bits, length); 
    } 

    public static void Write(ref ulong x, int length, int value) { 
     ulong mask = 0xFFFFFFFFFFFFFFFF >> (64 - length); 
     x = (x << length) | ((ulong)value & mask); 
    } 

    public static byte[] CopyBlock(byte[] bytes, int offset, int length) { 
     int startByte = offset/8; 
     int endByte = (offset + length - 1)/8; 
     int shiftA = offset % 8; 
     int shiftB = 8 - shiftA; 
     byte[] dst = new byte[(length + 7)/8]; 
     if (shiftA == 0) { 
      Buffer.BlockCopy(bytes, startByte, dst, 0, dst.Length); 
     } 
     else { 
      int i; 
      for (i = 0; i < endByte - startByte; i++) { 
       dst[i] = (byte)((bytes[startByte + i] << shiftA) | (bytes[startByte + i + 1] >> shiftB)); 
      } 
      if (i < dst.Length) { 
       dst[i] = (byte)(bytes[startByte + i] << shiftA); 
      } 
     } 
     dst[dst.Length - 1] &= (byte)(0xFF << ((dst.Length * 8) - length)); 
     return dst; 
    } 
} 

public static class BitConverterBE { 
    public static ulong ToUInt64(byte[] value, int startIndex) { 
     return 
      ((ulong)value[startIndex ] << 56) | 
      ((ulong)value[startIndex + 1] << 48) | 
      ((ulong)value[startIndex + 2] << 40) | 
      ((ulong)value[startIndex + 3] << 32) | 
      ((ulong)value[startIndex + 4] << 24) | 
      ((ulong)value[startIndex + 5] << 16) | 
      ((ulong)value[startIndex + 6] << 8) | 
      ((ulong)value[startIndex + 7]  ); 
    } 

    public static uint ToUInt32(byte[] value, int startIndex) { 
     return 
      ((uint)value[startIndex ] << 24) | 
      ((uint)value[startIndex + 1] << 16) | 
      ((uint)value[startIndex + 2] << 8) | 
      ((uint)value[startIndex + 3]  ); 
    } 

    public static ushort ToUInt16(byte[] value, int startIndex) { 
     return (ushort)(
      (value[startIndex ] << 8) | 
      (value[startIndex + 1]  )); 
    } 

    public static byte[] GetBytes(ulong value) { 
     byte[] buff = new byte[8]; 
     buff[0] = (byte)(value >> 56); 
     buff[1] = (byte)(value >> 48); 
     buff[2] = (byte)(value >> 40); 
     buff[3] = (byte)(value >> 32); 
     buff[4] = (byte)(value >> 24); 
     buff[5] = (byte)(value >> 16); 
     buff[6] = (byte)(value >> 8); 
     buff[7] = (byte)(value  ); 
     return buff; 
    } 

    public static byte[] GetBytes(uint value) { 
     byte[] buff = new byte[4]; 
     buff[0] = (byte)(value >> 24); 
     buff[1] = (byte)(value >> 16); 
     buff[2] = (byte)(value >> 8); 
     buff[3] = (byte)(value  ); 
     return buff; 
    } 

    public static byte[] GetBytes(ushort value) { 
     byte[] buff = new byte[2]; 
     buff[0] = (byte)(value >> 8); 
     buff[1] = (byte)(value  ); 
     return buff; 
    } 
} 

public static class BitConverterLE { 
    public static byte[] GetBytes(ulong value) { 
     byte[] buff = new byte[8]; 
     buff[0] = (byte)(value  ); 
     buff[1] = (byte)(value >> 8); 
     buff[2] = (byte)(value >> 16); 
     buff[3] = (byte)(value >> 24); 
     buff[4] = (byte)(value >> 32); 
     buff[5] = (byte)(value >> 40); 
     buff[6] = (byte)(value >> 48); 
     buff[7] = (byte)(value >> 56); 
     return buff; 
    } 

    public static byte[] GetBytes(uint value) { 
     byte[] buff = new byte[4]; 
     buff[0] = (byte)(value  ); 
     buff[1] = (byte)(value >> 8); 
     buff[2] = (byte)(value >> 16); 
     buff[3] = (byte)(value >> 24); 
     return buff; 
    } 

    public static byte[] GetBytes(ushort value) { 
     byte[] buff = new byte[2]; 
     buff[0] = (byte)(value  ); 
     buff[1] = (byte)(value >> 8); 
     return buff; 
    } 
} 

Ahora aplicar clase auxiliar de audio y la interfaz:

interface IAudioWriter 
    { 
    void WriteChunk(byte[] chunk, uint timeStamp); 
    void Finish(); 
    string Path { get; } 
} 

    class AACWriter : IAudioWriter 
    { 
    string _path; 
    FileStream _fs; 
    int _aacProfile; 
    int _sampleRateIndex; 
    int _channelConfig; 

    public AACWriter(string path) { 
     _path = path; 
     _fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, 65536); 
    } 

    public void WriteChunk(byte[] chunk, uint timeStamp) 
      { 
     if (chunk.Length < 1) return; 

     if (chunk[0] == 0) { // Header 
      if (chunk.Length < 3) return; 

      ulong bits = (ulong)BitConverterBE.ToUInt16(chunk, 1) << 48; 

      _aacProfile = BitHelper.Read(ref bits, 5) - 1; 
      _sampleRateIndex = BitHelper.Read(ref bits, 4); 
      _channelConfig = BitHelper.Read(ref bits, 4); 

      if ((_aacProfile < 0) || (_aacProfile > 3)) 
       throw new Exception("Unsupported AAC profile."); 
      if (_sampleRateIndex > 12) 
       throw new Exception("Invalid AAC sample rate index."); 
      if (_channelConfig > 6) 
       throw new Exception("Invalid AAC channel configuration."); 
     } 
     else { // Audio data 
      int dataSize = chunk.Length - 1; 
      ulong bits = 0; 

      // Reference: WriteADTSHeader from FAAC's bitstream.c 

      BitHelper.Write(ref bits, 12, 0xFFF); 
      BitHelper.Write(ref bits, 1, 0); 
      BitHelper.Write(ref bits, 2, 0); 
      BitHelper.Write(ref bits, 1, 1); 
      BitHelper.Write(ref bits, 2, _aacProfile); 
      BitHelper.Write(ref bits, 4, _sampleRateIndex); 
      BitHelper.Write(ref bits, 1, 0); 
      BitHelper.Write(ref bits, 3, _channelConfig); 
      BitHelper.Write(ref bits, 1, 0); 
      BitHelper.Write(ref bits, 1, 0); 
      BitHelper.Write(ref bits, 1, 0); 
      BitHelper.Write(ref bits, 1, 0); 
      BitHelper.Write(ref bits, 13, 7 + dataSize); 
      BitHelper.Write(ref bits, 11, 0x7FF); 
      BitHelper.Write(ref bits, 2, 0); 

      _fs.Write(BitConverterBE.GetBytes(bits), 1, 7); 
      _fs.Write(chunk, 1, dataSize); 
     } 
    } 

    public void Finish() { 
     _fs.Close(); 
    } 

    public string Path { 
     get { 
      return _path; 
     } 
    } 
} 

Ahora lo que hay que hacer por sí mismo es, leer fragmentos uno a uno de la primer archivo AAC y escríbalos, después de eso, lea los fragmentos uno por uno desde el segundo archivo AAC y añádalos al archivo intermedio.

Nota, el código anterior es C#, por lo que tendrá que utilizar el retorno para simular ref efecto de C# 's, simplemente mediante la sustitución:

ref Type variable_name 

con:

_<Type> variable_name 
+0

Incluso eso no va a ser suficiente, ya que hay una gran cantidad de metadatos MPEG4 (como la tabla de tamaño de muestra 'stsz') que deben combinarse de forma inteligente. – duskwuff

+0

Lo que estás sugiriendo viene en el alcance del contenedor MP4. 'ADTSHeader' en cada fragmento proporciona suficiente información para que el decodificador decodifique correctamente el audio, que es el archivo RAW de AAC. @duskwuff –

+0

La referencia a '" mdat "' en el código de ejemplo me hace pensar que en realidad es un MPEG4 con la extensión incorrecta. – duskwuff

1

Yo sugeriría usted para ver cómo se analizan los archivos aac en la biblioteca jaad, son particularmente interesantes el código de manejo ADTS here y el análisis de encabezado ADIF here.