自学内容网 自学内容网

05 - FFmpeg 提取 PCM 音频裸数据

----------------------------------------------------------------- PCM介绍 ----------------------------------------------------------------
PCM(Pulse Code Modulation),脉冲编码调制,是一种用数字表示采样模拟信号的方法。
核心过程:采样-->量化-->编码

----------------------------------------------------------------- PCM关键要素 -----------------------------------------------------------------
·采样率(sampleRate):每秒中采集样本的个数,如8KHz,表示每秒采样8000次。
奈奎斯特定理,明确:按比声音最高频率高2倍以上的频率对声音进行采样;
人耳能接受的频率范围为20Hz~20kHz,故采样率一般为44.1KHz较好,采样率越高,质量越高,但存储空间增大。
·量化格式(sampleFormat) : ffmpeg支持的量化格式: ffmpeg -formats | grep PCM

声道数"(channel):单声道(mono)、双声道(stereo)

----------------------------------------------------------------- PCM数据格式 -----------------------------------------------------------------
存储格式:
 > 双声道音频文件,采样数据按LRLR方式存储,存储的时候与字节序有关。
 > 单声道音频文件,采样数据按时间先后依次存入(有时也会用LRLR方式存储,但另一个声道数据为0)。
· 存储格式分为Packed和Planner两种,对于双通道音频,Packed为两个声道的数据交错存储;Planner 为两个声道数据分开存储:
 > Packed: LRLRLR
 > Planner: LLLRRR

----------------------------------------------------------------- PCM计算 -----------------------------------------------------------------
 · 大小计算:以CD的音质为例:量化格式为16比特(2字节),采样率为44100,声道数为2
    比特率为:44100*16*2=1378.125kbps
    1分钟音频大小:1378.125 * 60/8/1024=10.09MB
 · ffmpeg提取pcm数据命令:
    ffmpeg -i break.aac -ar 48000 -ac 2 -f s16le out.pcm
 · ffplay播放pcm数据:
    ffplay -ar 48000 -ac 2 -f s16le out.pcm

方法一


int decodeAudioInterface(AVCodecContext *decoderCtx, AVPacket *packet, AVFrame *frame, FILE *dest_fp)
{
    int ret = avcodec_send_packet(decoderCtx, packet);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "send packet to decoder failed:%s\n", av_err2str(ret));
        return -1;
    }
 
    while (ret >= 0)
    {
        ret = avcodec_receive_frame(decoderCtx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            av_log(NULL, AV_LOG_WARNING, "[decodeAudioInterface] -- AVERROR(EAGAIN) || AVERROR_EOF \n", av_err2str(ret));
            return 0;
        }
        else if (ret < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "decode packet failed:%s\n", av_err2str(ret));
        }
        // frame -- fltp
        int dataSize = av_get_bytes_per_sample(decoderCtx->sample_fmt);
        if (dataSize < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "get bytes failed!\n");
            return -1;
        }

        for (int i = 0; i < frame->nb_samples; i++)
        {
            for (int channel = 0; channel < decoderCtx->channels; channel++)
            {
                fwrite(frame->data[channel] + dataSize * i, 1, dataSize, dest_fp);
            }
        }
    }
    return 0;
}

int decodeAudio(const char *inFileName, const char *outFileName)
{
    /********************************************************************/
    FILE *dest_fp = fopen(outFileName, "wb+");
    if (dest_fp == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "open outfile %s failed!\n", outFileName);
        goto end;
    }
    /********************************************************************/

    AVFormatContext *inFmtCtx = NULL;
    int ret = avformat_open_input(&inFmtCtx, inFileName, NULL, NULL);
    if (ret != 0)
    {
        av_log(NULL, AV_LOG_ERROR, "open input file failed:%s\n", av_err2str(ret));
        return -1;
    }
    ret = avformat_find_stream_info(inFmtCtx, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "find stream info failed:%s\n", av_err2str(ret));
        goto end;
    }
    int audioIndex = av_find_best_stream(inFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audioIndex < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "find bast stream failed:%s\n", av_err2str(audioIndex));
        goto end;
    }
    AVCodecContext *decoderCtx = avcodec_alloc_context3(NULL);
    if (decoderCtx == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "avcodec alloc context failed\n");
        ret = -1;
        goto end;
    }
    // 拷贝编码参数
    avcodec_parameters_to_context(decoderCtx, inFmtCtx->streams[audioIndex]->codecpar);
    AVCodec *decoder = avcodec_find_decoder(decoderCtx->codec_id);
    if (decoder == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "find decoder %d failed!\n", decoderCtx->codec_id);
    }
    ret = avcodec_open2(decoderCtx, decoder, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "open decoder failed:%s\n", av_err2str(ret));
        goto end;
    }

    AVPacket packet;
    av_init_packet(&packet);
    AVFrame *frame = av_frame_alloc();
    int frameSize = av_samples_get_buffer_size(NULL, decoderCtx->channels, frame->nb_samples, decoderCtx->sample_fmt, 1);
    uint8_t *frameBuffer = av_malloc(frameSize);
    avcodec_fill_audio_frame(frame, decoderCtx->channels, decoderCtx->sample_fmt, frameBuffer, frameSize, 1);

    while (av_read_frame(inFmtCtx, &packet) >= 0)
    {
        if (packet.stream_index == audioIndex)
        {
            decodeAudioInterface(decoderCtx, &packet, frame, dest_fp);
        }
        av_packet_unref(&packet);
    }
    decodeAudioInterface(decoderCtx, NULL, frame, dest_fp);

end:
    if (inFmtCtx)
    {
        avformat_close_input(&inFmtCtx);
    }
    if (decoderCtx)
    {
        avcodec_free_context(&decoderCtx);
    }
    if (frame)
    {
        av_frame_free(&frame);
    }
    if (frameBuffer)
    {
        av_freep(frameBuffer);
    }
    if (dest_fp)
    {
        fclose(dest_fp);
    }

    return 0;
}

====================================================================================

方法二、

#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFLT_THRESH 4096

int decodeAudioInterface(AVCodecContext *decoderCtx, AVPacket *packet, AVFrame *frame, FILE *dest_fp)
{
    int ret = avcodec_send_packet(decoderCtx, packet);
    if (ret == AVERROR(EAGAIN))
    {
        av_log(NULL, AV_LOG_WARNING, "[decodeAudioInterface] -- AVERROR(EAGAIN) \n");
    }
    else if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "send packet to decoder failed: %s\n", av_err2str(ret));
        return -1;
    }

    while (ret >= 0)
    {
        // 对于frame avcodec_receive_frame 内部每次都先调用
        ret = avcodec_receive_frame(decoderCtx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            return;
        }
        else if (ret < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "decode packet failed:%s\n", av_err2str(ret));
        }
        // 获取单个sample 占用的字节
        size_t dataSize = av_get_bytes_per_sample(decoderCtx->sample_fmt);
        if (dataSize < 0)
        {
            /*This should not occur,checking just for paranoia*/
            av_log(NULL, AV_LOG_ERROR, "get bytes failed!\n");
            return -1;
        }
        av_log(NULL, AV_LOG_INFO,"采样率: %uHZ, 通道:%u, 编码格式:%u \n", frame->sample_rate,frame->channels, frame->format);
         
        for (int i = 0; i < frame->nb_samples; i++)
        {
            for (int channel = 0; channel < decoderCtx->channels; channel++) // 交错的方式写入,float的格式输出
            {
                fwrite(frame->data[channel] + dataSize * i, 1, dataSize, dest_fp);
            }
        }
    }
}

//  ffplay -ar 48000 -ac 2 -f f32le outTest.pcm
int decodeAudio(const char *inFileName, const char *outFileName)
{
    /************************************************************************************/
    // 打开输入文件
    FILE *inFile = fopen(inFileName, "rb");
    if (!inFile)
    {
        av_log(NULL, AV_LOG_ERROR, "Could not open:%s\n", inFileName);
        goto _end;
    }

    // 打开输出文件
    FILE *outFile = fopen(outFileName, "wb");
    if (!outFile)
    {
        av_log(NULL, AV_LOG_ERROR, "Could not open:%s\n", inFileName);
        goto _end;
    }

    uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t *data = inbuf;
    size_t dataSize = fread(inbuf, 1, AUDIO_INBUF_SIZE, inFile);
    /************************************************************************************/

    enum AVCodecID AudioCodecID = AV_CODEC_ID_AAC;
    if (strstr(inFileName, "aac") != NULL)
    {
        AudioCodecID = AV_CODEC_ID_AAC;
    }
    else if (strstr(inFileName, "mp3") != NULL)
    {
        AudioCodecID = AV_CODEC_ID_MP3;
    }
    else
    {
        av_log(NULL, AV_LOG_WARNING, "default codec id:%d\n", AudioCodecID);
    }

    // 查找解码器
    const AVCodec *decoder;
    decoder = avcodec_find_decoder(AudioCodecID); // AV_CODEC_ID_AAC
    if (!decoder)
    {
        av_log(NULL, AV_LOG_ERROR, "decoder not found\n");
        goto _end;
    }
    // 获得裸流的解析器 -- 根据制定的解码器ID初始化相应裸流的解析器
    AVCodecParserContext *parserCtx = av_parser_init(decoder->id);
    if (!parserCtx)
    {
        av_log(NULL, AV_LOG_ERROR, "parserCtx not found\n");
        goto _end;
    }
    AVCodecContext *decoderCtx = NULL;
    decoderCtx = avcodec_alloc_context3(decoder);
    if (!decoderCtx)
    {
        av_log(NULL, AV_LOG_ERROR, "Conld not allocate audio codec context\n");
        goto _end;
    }

    // 将解码器和解码器上下文进行关联
    int ret = avcodec_open2(decoderCtx, decoder, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Could not open codec\n");
        goto _end;
    }

    AVFrame *decodeFrame = av_frame_alloc();
    if (decodeFrame == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "Could not allocate audio decodeFrame\n");
        goto _end;
    }

    AVPacket *packet = NULL;
    packet = av_packet_alloc();
    while (dataSize > 0)
    {
        ret = av_parser_parse2(parserCtx, decoderCtx, &packet->data, &packet->size, data, dataSize, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
        if (ret < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "Error while parsing\n");
            goto _end;
        }
        data += ret;     // 跳过已经解析的数据
        dataSize -= ret; // 对应的缓存大小也做相应的减小

        if (packet->size)
            decodeAudioInterface(decoderCtx, packet, decodeFrame, outFile);

        if (dataSize < AUDIO_REFLT_THRESH) // 如果数据少了则再次读取
        {
            memmove(inbuf, data, dataSize); // 把之前剩的数据拷贝到 buffer 的其实位置
            data = inbuf;
            // 读取数据 长度: AUDIO_INBUF_SIZE - dataSize
            int len = fread(data + dataSize, 1, AUDIO_INBUF_SIZE - dataSize, inFile);
            if (len > 0)
                dataSize += len;
        }
    }

_end:
    if (outFile)
        fclose(outFile);
    if (inFile)
        fclose(inFile);
    if (decoderCtx)
        avcodec_free_context(&decoderCtx);
    if (parserCtx)
        av_parser_close(parserCtx);
    if (decodeFrame)
        av_frame_free(&decodeFrame);
    if (packet)
        av_packet_free(&packet);

    return 0;
}


原文地址:https://blog.csdn.net/weixin_44977283/article/details/140349472

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!