2014-06-10 9 views
7

Sto seguendo fortemente this Xamarin sample (basato su this Apple sample) per convertire un file LinearPCM in un file AAC.AudioConverter # FillComplexBuffer restituisce -50 e non converte nulla

L'esempio funziona benissimo, ma implementato nel mio progetto, il metodo FillComplexBuffer restituisce l'errore -50 e l'evento InputData non viene attivato una volta, quindi non viene convertito nulla.

L'errore viene visualizzato solo durante il test su un dispositivo. Quando si esegue il test sull'emulatore, tutto funziona alla grande e alla fine ottengo un buon file AAC codificato.

Ho provato un sacco di cose oggi, e non vedo alcuna differenza tra il mio codice e il codice di esempio. Hai idea di dove possa venire?

Non so se questo è in qualche modo collegato a Xamarin, non sembra così dato che l'esempio di Xamarin funziona alla grande.

Ecco la parte rilevante del mio codice:

protected void Encode(string path) 
{ 
    // In class setup. File at TempWavFilePath has DecodedFormat as format. 
    // 
    // DecodedFormat = AudioStreamBasicDescription.CreateLinearPCM(); 
    // AudioStreamBasicDescription encodedFormat = new AudioStreamBasicDescription() 
    // { 
    // Format = AudioFormatType.MPEG4AAC, 
    // SampleRate = DecodedFormat.SampleRate, 
    // ChannelsPerFrame = DecodedFormat.ChannelsPerFrame, 
    // }; 
    // AudioStreamBasicDescription.GetFormatInfo (ref encodedFormat); 
    // EncodedFormat = encodedFormat; 


    // Setup converter 
    AudioStreamBasicDescription inputFormat = DecodedFormat; 
    AudioStreamBasicDescription outputFormat = EncodedFormat; 

    AudioConverterError converterCreateError; 
    AudioConverter converter = AudioConverter.Create(inputFormat, outputFormat, out converterCreateError); 
    if (converterCreateError != AudioConverterError.None) 
    { 
    Console.WriteLine("Converter creation error: " + converterCreateError); 
    } 
    converter.EncodeBitRate = 192000; // AAC 192kbps 

    // get the actual formats back from the Audio Converter 
    inputFormat = converter.CurrentInputStreamDescription; 
    outputFormat = converter.CurrentOutputStreamDescription; 


    /*** INPUT ***/ 

    AudioFile inputFile = AudioFile.OpenRead(NSUrl.FromFilename(TempWavFilePath)); 

    // init buffer 
    const int inputBufferBytesSize = 32768; 
    IntPtr inputBufferPtr = Marshal.AllocHGlobal(inputBufferBytesSize); 

    // calc number of packets per read 
    int inputSizePerPacket = inputFormat.BytesPerPacket; 
    int inputBufferPacketSize = inputBufferBytesSize/inputSizePerPacket; 
    AudioStreamPacketDescription[] inputPacketDescriptions = null; 

    // init position 
    long inputFilePosition = 0; 

    // define input delegate 
    converter.InputData += delegate(ref int numberDataPackets, AudioBuffers data, ref AudioStreamPacketDescription[] dataPacketDescription) 
    { 
    // how much to read 
    if (numberDataPackets > inputBufferPacketSize) 
    { 
     numberDataPackets = inputBufferPacketSize; 
    } 

    // read from the file 
    int outNumBytes; 
    AudioFileError readError = inputFile.ReadPackets(false, out outNumBytes, inputPacketDescriptions, inputFilePosition, ref numberDataPackets, inputBufferPtr); 
    if (readError != 0) 
    { 
     Console.WriteLine("Read error: " + readError); 
    } 

    // advance input file packet position 
    inputFilePosition += numberDataPackets; 

    // put the data pointer into the buffer list 
    data.SetData(0, inputBufferPtr, outNumBytes); 

    // add packet descriptions if required 
    if (dataPacketDescription != null) 
    { 
     if (inputPacketDescriptions != null) 
     { 
     dataPacketDescription = inputPacketDescriptions; 
     } 
     else 
     { 
     dataPacketDescription = null; 
     } 
    } 

    return AudioConverterError.None; 
    }; 


    /*** OUTPUT ***/ 

    // create the destination file 
    var outputFile = AudioFile.Create (NSUrl.FromFilename(path), AudioFileType.M4A, outputFormat, AudioFileFlags.EraseFlags); 

    // init buffer 
    const int outputBufferBytesSize = 32768; 
    IntPtr outputBufferPtr = Marshal.AllocHGlobal(outputBufferBytesSize); 

    AudioBuffers buffers = new AudioBuffers(1); 

    // calc number of packet per write 
    int outputSizePerPacket = outputFormat.BytesPerPacket; 
    AudioStreamPacketDescription[] outputPacketDescriptions = null; 

    if (outputSizePerPacket == 0) { 
    // if the destination format is VBR, we need to get max size per packet from the converter 
    outputSizePerPacket = (int)converter.MaximumOutputPacketSize; 

    // allocate memory for the PacketDescription structures describing the layout of each packet 
    outputPacketDescriptions = new AudioStreamPacketDescription [outputBufferBytesSize/outputSizePerPacket]; 
    } 
    int outputBufferPacketSize = outputBufferBytesSize/outputSizePerPacket; 

    // init position 
    long outputFilePosition = 0; 

    long totalOutputFrames = 0; // used for debugging 

    // write magic cookie if necessary 
    if (converter.CompressionMagicCookie != null && converter.CompressionMagicCookie.Length != 0) 
    { 
    outputFile.MagicCookie = converter.CompressionMagicCookie; 
    } 

    // loop to convert data 
    Console.WriteLine ("Converting..."); 
    while (true) 
    { 
    // create buffer 
    buffers[0] = new AudioBuffer() 
    { 
     NumberChannels = outputFormat.ChannelsPerFrame, 
     DataByteSize = outputBufferBytesSize, 
     Data = outputBufferPtr 
    }; 

    int writtenPackets = outputBufferPacketSize; 

    // LET'S CONVERT (it's about time...) 
    AudioConverterError converterFillError = converter.FillComplexBuffer(ref writtenPackets, buffers, outputPacketDescriptions); 
    if (converterFillError != AudioConverterError.None) 
    { 
     Console.WriteLine("FillComplexBuffer error: " + converterFillError); 
    } 

    if (writtenPackets == 0) // EOF 
    { 
     break; 
    } 

    // write to output file 
    int inNumBytes = buffers[0].DataByteSize; 

    AudioFileError writeError = outputFile.WritePackets(false, inNumBytes, outputPacketDescriptions, outputFilePosition, ref writtenPackets, outputBufferPtr); 
    if (writeError != 0) 
    { 
     Console.WriteLine("WritePackets error: {0}", writeError); 
    } 

    // advance output file packet position 
    outputFilePosition += writtenPackets; 

    if (FlowFormat.FramesPerPacket != 0) { 
     // the format has constant frames per packet 
     totalOutputFrames += (writtenPackets * FlowFormat.FramesPerPacket); 
    } else { 
     // variable frames per packet require doing this for each packet (adding up the number of sample frames of data in each packet) 
     for (var i = 0; i < writtenPackets; ++i) 
     { 
     totalOutputFrames += outputPacketDescriptions[i].VariableFramesInPacket; 
     } 
    } 
    } 

    // write out any of the leading and trailing frames for compressed formats only 
    if (outputFormat.BitsPerChannel == 0) 
    { 
    Console.WriteLine("Total number of output frames counted: {0}", totalOutputFrames); 
    WritePacketTableInfo(converter, outputFile); 
    } 

    // write the cookie again - sometimes codecs will update cookies at the end of a conversion 
    if (converter.CompressionMagicCookie != null && converter.CompressionMagicCookie.Length != 0) 
    { 
    outputFile.MagicCookie = converter.CompressionMagicCookie; 
    } 

    // Clean everything 
    Marshal.FreeHGlobal(inputBufferPtr); 
    Marshal.FreeHGlobal(outputBufferPtr); 
    converter.Dispose(); 
    outputFile.Dispose(); 

    // Remove temp file 
    File.Delete(TempWavFilePath); 
} 

ho già visto this SO question, ma il non-dettagliata C++/Obj-C risposta correlata non sembra adattarsi con il mio problema.

Grazie!

+1

C'è qualche possibilità di pubblicare un progetto di esempio su github con il codice in errore? Sarebbe molto più facile eseguire il debug del semplice eyeballing. – bratsche

+0

[EDIT] Il repository GitHub è stato eliminato da quando ho trovato la risposta. – Ingolmo

risposta

0

Ho finalmente trovato la soluzione!

Ho appena dovuto dichiarare la categoria AVAudioSession prima di convertire il file.

AVAudioSession.SharedInstance().SetCategory(AVAudioSessionCategory.AudioProcessing); 
AVAudioSession.SharedInstance().SetActive(true); 

Dato che io uso anche un AudioQueue a RenderOffline, devo infatti impostare la categoria per AVAudioSessionCategory.PlayAndRecord così sia il rendering offline e l'audio di conversione lavoro.

Problemi correlati