Jun 17, 2011

Grabando Audio desde el microfono con FFmpeg + QtMultimedia


En este nuevo tutorial vamos a ver una de las cartas más difíciles de encontrar en Internet, vamos a mostrar como combinar FFmpeg, el framework de transcoding más avanzado del mundo, con Qt, el framework de interfaces gráficas mas avanzado del mundo, con el fin de grabar audio del micrófono a cualquier formato de archivo multimedia, en este caso usaremos el formato Ogg, el cual es un formato contenedor multimedia (audio, video y subtitulos), de uso e implementación libre, libre de regalías, y libre de DRM.
Y antes de empezar haremos una pequeña introducción al sonido digital.
IMPORTANTE

Tras leer este tutorial, recomiendo encarecidamente leer la actualización de este tema.


Aclaro antes que nada que como la teoría es bastante larga, y que puede que llegue a comerme alguna que otra explicación :s , al final del post voy a dejar varios enlaces de referencia para que puedan completar toda la teoría aquí expuesta, y por supuesto, como siempre, el código fuente completo del ejemplo, que es mucho más simple que toda esta explicación :P
Primero veamos la definición de Sonido y Audio Digital según Wikipedia.

Sonido


El sonido, en física, es cualquier fenómeno que involucre la propagación en forma de ondas elásticas (sean audibles o no), generalmente a través de un fluido (u otro medio elástico) que esté generando el movimiento vibratorio de un cuerpo.
El sonido humanamente audible consiste en ondas sonoras que producen oscilaciones de la presión del aire, que son convertidas en ondas mecánicas en el oído humano y percibidas por el cerebro. La propagación del sonido es similar en los fluidos, donde el sonido toma la forma de fluctuaciones de presión. En los cuerpos sólidos la propagación del sonido involucra variaciones del estado tensional del medio.
La propagación del sonido involucra transporte de energía sin transporte de materia, en forma de ondas mecánicas que se propagan a través de la materia sólida, líquida o gaseosa. Como las vibraciones se producen en la misma dirección en la que se propaga el sonido, se trata de una onda longitudinal.
El sonido es un fenómeno vibratorio transmitido en forma de ondas. Para que se genere un sonido es necesario que vibre alguna fuente. Las vibraciones pueden ser transmitidas a través de diversos medios elásticos, entre los más comunes se encuentran el aire y el agua. La fonética acústica concentra su interés especialmente en los sonidos del habla: cómo se generan, cómo se perciben, y cómo se pueden describir gráfica y/o cuantitativamente.

Audio digital


El audio digital es la codificación digital de una señal eléctrica que representa una onda sonora. Consiste en una secuencia de valores enteros y se obtienen de dos procesos: el muestreo y la cuantificación digital de la señal eléctrica.
El muestreo consiste en fijar la amplitud de la señal eléctrica a intervalos regulares de tiempo (tasa de muestreo). Para cubrir el espectro audible (20 a 20000 Hz) suele bastar con tasas de muestreo de algo más de 40000 Hz (el estándar CD-Audio emplea una tasa un 10% mayor con objeto de contemplar el uso de filtros no ideales), con 32000 muestras por segundo se tendría un ancho de banda similar al de la radio FM o una cinta de casete, es decir, permite registrar componentes de hasta 15 kHz, aproximadamente. Para reproducir un determinado intervalo de frecuencias se necesita una tasa de muestreo de poco más del doble (Teorema de muestreo de Nyquist-Shannon). Por ejemplo en los CDs, que reproducen hasta 20 kHz, emplean una tasa de muestreo de 44,1 kHz (frecuencia Nyquist de 22,05 kHz).
La cuantificación consiste en convertir el nivel de las muestra fijadas en el proceso de muestreo, normalmente, un nivel de tensión, en un valor entero de rango finito y predeterminado. Por ejemplo, utilizando cuantificación lineal, una codificación lineal de 8 bits discriminará entre 256 niveles de señal equidistantes. También se pueden hacer cuantificaciones no lineales, como es el caso de cuantificadores logarítmicos como la Ley Mu o la Ley A, que, a modo de ejemplo, aún usando 8 bits funcionan perceptualmente como 10 bits lineales para señales de baja amplitud en promedio, como la voz humana por ejemplo.
El formato más usado de audio digital PCM lineal es el del CD de audio: 44,1 kHz de tasa de muestreo y cuantificación lineal de 16 bits (que mide 65536 niveles de señal diferentes) y que, en la práctica, permite registrar señales analógicas con componentes hasta los 20 kHz y con relaciones señal a ruido de más de 90 dB.


Básicamente lo que se hace es convertir las ondas sonoras, energía mecánica en, energía eléctrica, luego un conversor análogo a digital convierte la Tensión eléctrica (diferencia de potencial eléctrico, en Volts) en un determinado instante en su correspondiente representación en bytes, para que se entienda un poco mejor, en la siguiente tabla podemos ver la relación que existe entre el nivel de sonido, la amplitud de la señal, la tensión eléctrica y su representación numérica:







Nivel de sonidoAmplitud de la señalTensión electricaRepresentación numerica
BajoPequeñaBajaNumero bajo
AltoGrandeAltaNumero alto


Una vez que el nivel de señal se a convertido a su representación numérica correspondiente, un reloj se encarga de recoger estas muestras digitales periódicamente y depositarlas en algún buffer para que luego puedan ser procesadas por el sistema y los programas, ese buffer puede ser por ejemplo algún área de la memoria ram, un archivo en el disco duro, etc.


Prueba rápida


Ahora vamos a ver un ejemplo sencillo de como podemos ver las muestras de audio en bruto que tomamos directamente desde el micrófono en la terminal usando el sistema operativo GNU/Linux.
GNU/Linux es un sistema operativo tipo Unix, y como tal esta basado en la idea de que todos los dispositivos físicos (pej. el mouse, el teclado, la tarjeta de sonido, la tarjeta de video, etc) están mapeados en archivos virtuales y que por lo tanto podemos podemos comunicarnos con estos dispositivos usando simples operaciones de lectura y escritura de archivos.
Vamos a poner todo lo dicho en practica, primero necesitamos instalar el paquete alsa-oss, Por ejemplo si estamos en Ubuntu o Debian, lo instalaremos con el siguiente comando:
apt-get install alsa-oss
Si estamos en Arch, lo instalamos así:
pacman -S alsa-oss
Luego cargamos el modulo para capturar sonido desde el micrófono por defecto usando OSS:
sudo modprobe snd_pcm_oss
Y finalmente hagamos que la magia aparezca ante nuestros ojos XD :
sudo cat /dev/dsp
No se asusten si ven caracteres raros, eso es lo normal, esos son los datos en bruto que el sistema recoje desde el buffer de audio, podemos apreciar que estos datos se renuevan cada determinado tiempo y que el tamaño de los datos obtenidos es también variable, pues el sistema operativo obtiene estos datos de forma asíncrona. El tamaño en bytes de las muestras obtenidas esta dado por:
$$tamaño\_muestras(bytes)=numero\_canales \times resolución\_muestra(bytes) \times numero\_muestras$$ Donde:

numero_canales: La cantidad de fuentes de audio de las que se tomara el sonido, por lo general suele ser 1 o 2.
resolución_muestra(bytes): La cantidad de bytes que se usaren para representar la amplitud de la señal en cada instante, por lo general 1 (8 bits), 2 (16 bits) o 4 (32 bits). FFmpeg sólo acepta muestra de 2 (16 bits)
numero_muestras: La cantidad de muestras recogidas hasta el momento.

Capturando los frames de audio con QtMultimedia


Por supuesto, que el ejemplo anterior era sólo aplicable a GNU/Linux, pero gracias a Qt y al modulo de QtMultimedia, podemos aplicar el mismo concepto indistintamente del sistema operativo que usemos.
Comencemos viendo un ejemplo trivial de como obtener estos frames de audio, comenzamos creando la clase para la captura de audio:
#include <QObject>
#include <QAudioInput>

// Creamos una clase para capturar los frames de audio.
class AudioCapture: public QObject
{
    Q_OBJECT

    public:
        explicit AudioCapture(QObject *parent = 0);

    private:
        // audio es el objeto que usaremos para configurar
        // los parámetros del dispositivo de entrada de audio
        // (en este caso el micrófono).
        QAudioInput *audio;

        // input_stream es el Buffer del dispositivo (archivo de dispositivo)
        // donde se acumulan las muestras de audio.
        QIODevice *input_stream;

    public slots:
        // Este slot será llamado cada vez que obtengamos un frame de audio.
        void getAudioFrame();
};
Y aquí definimos la clase:
#include <QAudioFormat>
#include <QAudioDeviceInfo>
#include <QtDebug>

AudioCapture::AudioCapture(QObject *parent): QObject(parent)
{
}

// Aquí configuramos la entrada de audio, en este caso el micrófono.
void AudioCapture::startRecord()
{
    // Obtenemos los parámetros de configuración del
    // dispositivo de captura de audio por defecto.
    QAudioDeviceInfo device_info = QAudioDeviceInfo::defaultInputDevice();

    // Imprimimos dichas configuraciones.
    // Aquí podemos los parametros por defecto del dispositivo de entrada de audio.
    qDebug() << "supportedByteOrders() = " << device_info.supportedByteOrders();
    qDebug() << "supportedChannelCounts() = " << device_info.supportedChannelCounts();
    qDebug() << "supportedCodecs() = " << device_info.supportedCodecs();
    qDebug() << "supportedSampleRates() = " << device_info.supportedSampleRates();
    qDebug() << "supportedSampleSizes() = " << device_info.supportedSampleSizes();
    qDebug() << "supportedSampleTypes() = " << device_info.supportedSampleTypes();
    qDebug();

    // Por lo general los parametros por defecto pueden no ser los
    // adecuados para la captura de audio, y por ello tenemos la
    // posibilidad de cambiarlos.
    QAudioFormat format = device_info.preferredFormat();
    format.setCodec("audio/pcm");
    format.setChannelCount(2);
    format.setSampleRate(8000);
    format.setSampleSize(16);
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    // Obtenemos el dispositivo de captura.
    audio = new QAudioInput(format);

    // Comienza a capturar sonido del microfono.
    input_stream = audio->start();

    // Obtengo las nuevas muestras de audio usando el modo push.
    connect(input_stream, SIGNAL(readyRead()), this, SLOT(getAudioFrame()));
}

// Aquí obtenemos y encodeamos las muestras de audio.
void AudioCapture::getAudioFrame()
{
    // Obtengo la cantidad de bytes acumulados hasta el momento.
    int bytes_ready = audio->bytesReady();

    // Y aquí obtengo los frames de audio en bruto
    QByteArray bytes_buff = input_stream->read(bytes_ready);

    // Y aquí mostramos esos datos en bruto.
    // el resultado será identico al de hacer cat
    // del dispositivo de audio en GNU/Linux pero
    // siendo completamente independiente del
    // sistema operativo.
    qDebug() << bytes_buff;
}
Ahora que logramos otener los frames de audio, pasemos a como hacer para encodearlos y grabarlos en un archivo de audio, para su posterior reprodución.
Para poder hacer el encoding de estos frames de audio utilizaremos FFmpeg, o más precisamente su API.
Según Wikipedia:

FFmpeg


FFmpeg es una colección de software libre que puede grabar, convertir y hacer streaming de audio y vídeo. Incluye libavcodec, una biblioteca de códecs. FFmpeg está desarrollado en GNU/Linux, pero puede ser compilado en la mayoría de los sistemas operativos, incluyendo Windows. El proyecto comenzó por Gerard Lantau, un seudónimo de Fabrice Bellard, y ahora es mantenido por Michael Niedermayer. Es destacable que la mayoría de los desarrolladores de FFmpeg lo sean también del proyecto MPlayer, más un miembro del proyecto Xine y que FFmpeg esté hospedado en el servidor del proyecto MPlayer.
FFmpeg está liberado bajo una licencia GNU Lesser General Public License 2.1+ o GNU General Public License 2+ (dependiendo de cuáles bibliotecas estén incluidas). Los desarrolladores recomiendan utilizar el último snapshot de Subversion ya que mantienen constantemente una versión estable.
Aqui podemos encontrar una lista bastante completa de proyectos que utilizan FFmpeg, tanto para reproducir como para grabar audio y video.
Entre los proyectos más destacables encontramos a: Blender (La maxíma suite libre de diseño 3D), Google Chrome (el navegador web de Google), Gnash (la alternativa libre a Flash), Cinelerra (editor de video de calidad profecional), Kdenlive (editor de video hogareño) y VLC (uno de los reproductores de video más famosos y utilizados en el mundo).
Ahora ya conocen el secreto de los grandes ;)
Pero resulta que, aún habiendo tantos programas basados en FFmpeg, es notable la gran carencia de ejemplos triviales (los cuales son más fáciles de comprender), y su API poco documentada. Y además de que practicamente no hay ejemplos secillos sobre lo quevamos a ver. Y por ello he decidido documentarlo en mi blog.
Obviamente, he estado buscando y documentandome varias semanas a cerca de como usar la API de FFmpeg y hasta ahora lo mejor que he podido encontrar a sido el mismo código fuente de FFmpeg, en especial estos dos archivos:
Veamos un ejemplo "trivial" de como grabar sonido a un archivo usando el formato contenedor Ogg:
/*
 * Para compilar este ejemplo:
 *
 * gcc -lavformat -o oggenc oggenc.c
 */

/* Incluimos las librerías necesarias para utilizar la API de FFmpeg */
#include <libavformat/avformat.h>

#define CHANNEL_COUNT 2  /* Cantidad de fuentes de audio */
#define SAMPLE_SIZE 16   /* FFmpeg sólo acepta muestras de 2 bytes */
#define SAMPLE_RATE 8000 /* Utilizamos una frecuencia de muestre de 8kHz */
#define N_FRAMES 100     /* Numero de frames a grabar (aprox. 10 seg.) */

#define OUTPUT_FILENAME "output.ogg"

int main(int argc, char *argv[])
{
    AVFormatContext *oc;
    AVStream *audio_st;
    AVOutputFormat *fmt;
    int audio_outbuf_size;
    int audio_sample_size;
    uint8_t *audio_outbuf;
    short int *samples;
    unsigned int i;
    AVPacket pkt;
    int frame;
    int channel;
    int sample;
    short int A;

    /* Registramos todos los codecs
       y formatos disponibles. */
    av_register_all();

    /* La funcion av_guess_format(const char *extencion, const char *nombre_del_archivo, const char *tipo_mime);
       devuelve el formato del archivo de salida con la combinacion de codecs de audio y video por defecto,
       esta combinación de codecs se determina a partir de la extención, el nombre del archivo
       o el identificador de recurso (tipo mime). */
    fmt = av_guess_format(NULL, OUTPUT_FILENAME, NULL);

    /* Creamos el contexto para el archivo de salida
       y le asignamos el formato por defecto. */
    oc = avformat_alloc_context();
    oc->oformat = fmt;

    /* Asignamos el nombre del archivo de salida al contexto del archivo. */
    snprintf(oc->filename, sizeof(oc->filename), "%s", OUTPUT_FILENAME);

    /* Agregamos un nuevo flujo de datos, en este caso audio,
       al archivo de salida usando los codecs por defecto. */
    audio_st = av_new_stream(oc, 1);

    /* Configuramos los parametros del codec. */
    audio_st->codec->codec_id = fmt->audio_codec;
    audio_st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
    audio_st->codec->sample_fmt = AV_SAMPLE_FMT_S16;
    audio_st->codec->bit_rate = CHANNEL_COUNT * SAMPLE_SIZE * SAMPLE_RATE;
    audio_st->codec->sample_rate = SAMPLE_RATE;
    audio_st->codec->channels = CHANNEL_COUNT;

    /* Algunos formatos necesitan que la cabecera
       este separada del flujo de datos. */
    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
        audio_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;

    /* Establece los parametros de salida
       (se debe realizar aungue no haya parametros). */
    av_set_parameters(oc, NULL);

    /* Se hace el v olcado del archivo de salida. */
    av_dump_format(oc, 0, OUTPUT_FILENAME, 1);

    /* Busca y abre el codec de audio por defecto. */
    avcodec_open(audio_st->codec, avcodec_find_encoder(audio_st->codec->codec_id));

    /* Aloja memoria para la salida codificada de audio. */
    audio_outbuf_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
    audio_outbuf = (uint8_t *)av_malloc(audio_outbuf_size);

    /* Calculamos el tamaño en bytes de la muestra y alojamos el buffer para las muestras. */
    audio_sample_size = 2 * audio_st->codec->frame_size * audio_st->codec->channels;
    samples = (short int *)av_malloc(audio_sample_size);

    /* Abre el archivo de salida como sólo escritura. */
    if (!(fmt->flags & AVFMT_NOFILE))
        avio_open(&oc->pb, OUTPUT_FILENAME, URL_WRONLY);

    /* Escribe la cabecera del archivo. */
    av_write_header(oc);

    for (sample = 0; sample < N_FRAMES; sample++)
    {
        /* Creamos un tono de forma senoidal identico para ambos canales */
        for (frame = 0; frame < audio_st->codec->frame_size; frame++)
        {
            A = 0x7FFF * sin(5 * 2 * M_PI * (float)frame / (float)N_FRAMES);

            for (channel = 0; channel < audio_st->codec->channels; channel++)
                samples[2 * frame + channel] = A;
        }

        /* Creamos e inicializamos
           el paquete de audio. */
        av_init_packet(&pkt);

        /* Codificamos las muestras de audio y las guardamos en el buffer de audio codificado de salida.
           FFmpeg sólo acepta buffers de ancho fijo.
           En el ejemplo completo Reemplazaremos la variable samples por un QByteArray donde se
           guardaran las muestras de audio de tamaño fijo */
        pkt.size = avcodec_encode_audio(audio_st->codec, audio_outbuf, audio_outbuf_size, samples);

        /* Configuramos la marca de tiepo de presentación de marca de tiempo, el momento en que el paquete descomprimido
           será presentado al usuario. */
        if (audio_st->codec->coded_frame && audio_st->codec->coded_frame->pts != (unsigned int)AV_NOPTS_VALUE)
            pkt.pts = av_rescale_q(audio_st->codec->coded_frame->pts, audio_st->codec->time_base, audio_st->time_base);

        /* Agregamos el resto de la información al paquete. */
        pkt.flags |= AV_PKT_FLAG_KEY;
        pkt.stream_index = audio_st->index;
        pkt.data = audio_outbuf;

        /* Escribimos el frame comprimido en el archivo de salida. */
        av_interleaved_write_frame(oc, &pkt);
    }

    /* escribir el tráiler, si lo hubiera. el tráiler debe ser escrito
       antes de cerrar los contextos de los codecs abiertos cuando
       escribió la cabecera, de lo contrario write_trailer puede tratar
       de utilizar la memoria que fue liberado en av_codec_close(). */
    av_write_trailer(oc);

    /* Cerramos el codec y liberamos el buffer de audio. */
    if (audio_st)
    {
        avcodec_close(audio_st->codec);
        av_free(audio_outbuf);
        av_free(samples);
    }

    /* Liberamos todos los flujos de audio. */
    for (i = 0; i < oc->nb_streams; i++)
    {
        av_freep(&oc->streams[i]->codec);
        av_freep(&oc->streams[i]);
    }

    /* Cerremos el archivo de salida. */
    if (!(fmt->flags & AVFMT_NOFILE))
        avio_close(oc->pb);

    /* Y liberamos el contexto del archivo de salida. */
    av_free(oc);

    return 0;
}
En este caso hemos escogido el formato contenedor Ogg, veamos porque. Según Wikipedia:

Ogg


Ogg es un formato libre de patentes y abierto, diseñado para dar un alto grado de eficiencia en el "streaming" y la compresión de archivos, y creado por la fundación Xiph.org.
Como con la mayoría de formatos contenedores, Ogg encapsula datos no comprimidos y permite la interpolación de los datos de audio y de vídeo dentro de un solo formato conveniente.
El nombre "Ogg" por lo tanto se refiere al formato de archivo el cual incluye un número de códecs separados e independientes de vídeo y audio, ambos desarrollados en código abierto. Los archivos terminados en la extensión ".ogg" pueden ser de cualquier tipo de archivo Ogg, audio o vídeo, aunque existe la recomendación de renombrarlos con la extensión ".oga" para audio y ".ogv" para video.
Ya que su uso está libre de patentes, varios códecs de Ogg han sido incluidos en muchos reproductores multimedia (VLC, mplayer, etc.) existiendo incluso filtros para reproducir los códecs Ogg en prácticamente cualquier reproductor que soporte DirectShow (Windows Media Player, BSplayer, Winamp, etc.).
Lo que lo convierte en un excelente formato para la grabación de audio y video si estamos desarrollando Software Libre.

Código fuente


Y uniendo toda la información, presento el código fuente completo del ejemplo:

Qt Audio Recorder

Enlaces de referencia

13 comments:

  1. Gracias por la explicacion de este methodo. De pura casualidad no tienen el decoder para ogg? :)

    ReplyDelete
  2. ¿Te referís al caso inverso? o sea ¿obtener los frames de audio con FFmpeg? ¿o a libogg?

    ReplyDelete
  3. Yo creo que si; quiero obtener los samples de audio de los bits compresados gracias a ogg.

    ReplyDelete
  4. Si usas la API de FFmpeg bajate el snapshot de FFmpeg y mira el ejemplo que esta en ffmpeg/doc/examples/decoding_encoding.c en la función audio_decode_example(), con eso podes obtener los frames de audio, en ese caso obligatoriamente tenes que incluir la misma copia de FFmpeg con la que hiciste las pruebas, porque sino puede pasar que entre cada snapshot, la API cambia completamente y el código deje de funcionar como me paso a mí :(
    Sino otra posibilidad es usando tuberías, decodificas el archivo de audio usando FFmpeg y enviás los frames de audio en formato raw a una tubería y luego desde tu programa lees los frames desde la tubería como si fuera un archivo normal. Revisá mi otro articulo para que te des una idea.
    Ya con eso no tendrías problemas con los cambios de versión de FFmpeg porque los parámetros seguirían siendo los mismos.

    ReplyDelete
  5. Hola Hipersayanx,

    muy buen tuto!
    Tengo una duda que quizas puedas ayudarme, necesito hacer una especie de audio level, medir el volumen de la voz o algo similar.

    El tema es el siguiente como hago yo para encodear el frame de la funcion "void AudioCapture::getAudioFrame()" en flac (es el formato que necesito), es decir necesito que al hablar y hacer un silencio se guarde en un archivo en formato flac.

    Se puede hacer esto utilizando QAudioInput?? seria ideal porque es un proyecto que tengo para portalo a Meego (nokia n9) usando qt. Si tenes alguna idea de como hacerlo o si conoces algun sitio con info me vendria genial! Estoy empezando con esto del audio y demas, me seria de buena ayuda tu respuesta, un abrazo!

    ReplyDelete
  6. ¿Sos el mismo de esta pregunta? Una vez que obtenés el frame de audio con input_stream->read(bytes_ready), ese mismo frame lo mandás al encoder, pej. a FFmpeg, o también podes enviar ese frame a flac.

    ReplyDelete
  7. Gracias hipersayan_x, estoy comenzando con Qt creator y esto me parece muy interesante

    ReplyDelete
  8. Hola, soy un hereje ya que programo Qt en windows (je je) y tengo el problema de los codecs. Ya que averiguando encontre que QtMultimedia usa DShow y este sistema solo tiene PCM, o sea wav. Por lo que no encuentro la forma de grabar directamente en MP3 como lo hago en linux. Probe con QtMel y Qtav pero no andan bien en windows y con ffmpeg tampoco, ya que si grabo el wav de 1 hora me quedan 600mb en buffer que tardaria muchisimo en convertir. Sabes alguna solucion? Gracias

    ReplyDelete
    Replies
    1. En principio Qt tiene la clase QMediaRecorder pero la interfaz que ofrece es muy pobre, y paso de usar QtMultimedia en general (malas experiencias). QtMEL no lo he probado así que no puedo dar una opinión, y QtAV, hasta donde yo sé es únicamente para reproducción. Y de plano podemos descartar DShow y MediaFoundation por no ser multiplataforma, y por que además MMF no está del todo implementada en MinGW.

      Con lo que en mí opinión las únicas opciones que quedan son FFmpeg y GStreamer, con FFmpeg he trabajado bastante aunque todavía no he probado en grabar en Windows, pero imagino que debe tener el mismo rendimiento que en GNU/Linux. Con GStreamer, sé que se puede compilar con MinGW, hace poco empece a hacer algunas pruebas, pero va a pasar un tiempo hasta que tenga algo funcional.

      Ahora bien, volviendo al tema original, para encodear en formato MP3 necesitas el codec libmp3lame, este codec tiene algunas restricciones que el audio que quieras grabar debe cumplir, por ejemplo, la frecuencia de muestreo del audio de entrada debe debe ser alguna de las siguientes:

      48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000

      y los formatos de las muestra de entrada pueden ser fltp (flotante de 32 bits planar), s32p (entero con signo de 32 bits planar) o s16p (entero con signo de 16 bits planar). Toda esa información la puedes obtener de la estructura AVCodec:

      audio_st->codec->codec->supported_samplerates
      audio_st->codec->codec->sample_fmts
      audio_st->codec->codec->channel_layouts

      También tienes la limitación del tamaño del buffer de audio que puedes escribir, lo puedes consultar desde audio_st->codec->frame_size.

      Una vez que obtienes esa información, conviertes el audio de entrada usando SWResample y se lo pasas a avcodec_encode_audio2.

      Otro detalle, es que si en el buffer quedan 600mb es muy probablemente que los valores de PTS están mal colocados.

      Y para finalizar, ahora que lo veo voy a tener actualizar el código, por que este usa la API vieja, voy a ver si lo puedo subir en unas horas o a mas tardar esta semana.

      Delete
    2. Gracias x la ayuda, pero bueno, revisando el wav mas optimizado pesa 230~300 mb , y si despues de generar el wav lo convierto con el ffmpeg con qprocess el proceso tarda 1 minuto solamente, asi que lo puedo dar por aceptable. Lo unico es que tendria que implementar algo para parsear la salida del ffmpeg y ponerlo en un Progressbar o algo parecido.
      Gracias

      Delete
    3. Es lógico, en FFmpeg el formato WAV almacena por defecto las muestras en formato pcm_s16le, o dicho de otro modo, el audio en bruto. Aunque también puedes optar por otros codecs usando

      fmt->audio_codec = avcodec_find_encoder_by_name("codec");

      Delete
    4. El wav lo grabo con QAudioRecorder, con todas las opciones x defecto, pero estaria buenisimo que los frames que obtenga los pudiera usar con el avcodec_encode_audio2(), solo que no encuentro ningun ejemplo completo en la web de como hacerlo.

      Delete
    5. Sí, lo sé, por lo general con todas las configuraciones especiales que tiene cada codec es muy difícil hacer un ejemplo trivial, por que a veces hay que meterle algún parche para que funcione con tal o cual codec. Además de que los devs modifican bastante seguido la API. Por lo general, el mejor ejemplo de como usar la API de FFmpeg es el mismo FFmpeg.

      Delete