• 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

    方法一

    1. int decodeAudioInterface(AVCodecContext *decoderCtx, AVPacket *packet, AVFrame *frame, FILE *dest_fp)
    2. {
    3. int ret = avcodec_send_packet(decoderCtx, packet);
    4. if (ret < 0)
    5. {
    6. av_log(NULL, AV_LOG_ERROR, "send packet to decoder failed:%s\n", av_err2str(ret));
    7. return -1;
    8. }
    9. while (ret >= 0)
    10. {
    11. ret = avcodec_receive_frame(decoderCtx, frame);
    12. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
    13. {
    14. av_log(NULL, AV_LOG_WARNING, "[decodeAudioInterface] -- AVERROR(EAGAIN) || AVERROR_EOF \n", av_err2str(ret));
    15. return 0;
    16. }
    17. else if (ret < 0)
    18. {
    19. av_log(NULL, AV_LOG_ERROR, "decode packet failed:%s\n", av_err2str(ret));
    20. }
    21. // frame -- fltp
    22. int dataSize = av_get_bytes_per_sample(decoderCtx->sample_fmt);
    23. if (dataSize < 0)
    24. {
    25. av_log(NULL, AV_LOG_ERROR, "get bytes failed!\n");
    26. return -1;
    27. }
    28. for (int i = 0; i < frame->nb_samples; i++)
    29. {
    30. for (int channel = 0; channel < decoderCtx->channels; channel++)
    31. {
    32. fwrite(frame->data[channel] + dataSize * i, 1, dataSize, dest_fp);
    33. }
    34. }
    35. }
    36. return 0;
    37. }
    1. int decodeAudio(const char *inFileName, const char *outFileName)
    2. {
    3. /********************************************************************/
    4. FILE *dest_fp = fopen(outFileName, "wb+");
    5. if (dest_fp == NULL)
    6. {
    7. av_log(NULL, AV_LOG_ERROR, "open outfile %s failed!\n", outFileName);
    8. goto end;
    9. }
    10. /********************************************************************/
    11. AVFormatContext *inFmtCtx = NULL;
    12. int ret = avformat_open_input(&inFmtCtx, inFileName, NULL, NULL);
    13. if (ret != 0)
    14. {
    15. av_log(NULL, AV_LOG_ERROR, "open input file failed:%s\n", av_err2str(ret));
    16. return -1;
    17. }
    18. ret = avformat_find_stream_info(inFmtCtx, NULL);
    19. if (ret < 0)
    20. {
    21. av_log(NULL, AV_LOG_ERROR, "find stream info failed:%s\n", av_err2str(ret));
    22. goto end;
    23. }
    24. int audioIndex = av_find_best_stream(inFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    25. if (audioIndex < 0)
    26. {
    27. av_log(NULL, AV_LOG_ERROR, "find bast stream failed:%s\n", av_err2str(audioIndex));
    28. goto end;
    29. }
    30. AVCodecContext *decoderCtx = avcodec_alloc_context3(NULL);
    31. if (decoderCtx == NULL)
    32. {
    33. av_log(NULL, AV_LOG_ERROR, "avcodec alloc context failed\n");
    34. ret = -1;
    35. goto end;
    36. }
    37. // 拷贝编码参数
    38. avcodec_parameters_to_context(decoderCtx, inFmtCtx->streams[audioIndex]->codecpar);
    39. AVCodec *decoder = avcodec_find_decoder(decoderCtx->codec_id);
    40. if (decoder == NULL)
    41. {
    42. av_log(NULL, AV_LOG_ERROR, "find decoder %d failed!\n", decoderCtx->codec_id);
    43. }
    44. ret = avcodec_open2(decoderCtx, decoder, NULL);
    45. if (ret < 0)
    46. {
    47. av_log(NULL, AV_LOG_ERROR, "open decoder failed:%s\n", av_err2str(ret));
    48. goto end;
    49. }
    50. AVPacket packet;
    51. av_init_packet(&packet);
    52. AVFrame *frame = av_frame_alloc();
    53. int frameSize = av_samples_get_buffer_size(NULL, decoderCtx->channels, frame->nb_samples, decoderCtx->sample_fmt, 1);
    54. uint8_t *frameBuffer = av_malloc(frameSize);
    55. avcodec_fill_audio_frame(frame, decoderCtx->channels, decoderCtx->sample_fmt, frameBuffer, frameSize, 1);
    56. while (av_read_frame(inFmtCtx, &packet) >= 0)
    57. {
    58. if (packet.stream_index == audioIndex)
    59. {
    60. decodeAudioInterface(decoderCtx, &packet, frame, dest_fp);
    61. }
    62. av_packet_unref(&packet);
    63. }
    64. decodeAudioInterface(decoderCtx, NULL, frame, dest_fp);
    65. end:
    66. if (inFmtCtx)
    67. {
    68. avformat_close_input(&inFmtCtx);
    69. }
    70. if (decoderCtx)
    71. {
    72. avcodec_free_context(&decoderCtx);
    73. }
    74. if (frame)
    75. {
    76. av_frame_free(&frame);
    77. }
    78. if (frameBuffer)
    79. {
    80. av_freep(frameBuffer);
    81. }
    82. if (dest_fp)
    83. {
    84. fclose(dest_fp);
    85. }
    86. return 0;
    87. }

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

    方法二、

    1. #define AUDIO_INBUF_SIZE 20480
    2. #define AUDIO_REFLT_THRESH 4096
    1. int decodeAudioInterface(AVCodecContext *decoderCtx, AVPacket *packet, AVFrame *frame, FILE *dest_fp)
    2. {
    3. int ret = avcodec_send_packet(decoderCtx, packet);
    4. if (ret == AVERROR(EAGAIN))
    5. {
    6. av_log(NULL, AV_LOG_WARNING, "[decodeAudioInterface] -- AVERROR(EAGAIN) \n");
    7. }
    8. else if (ret < 0)
    9. {
    10. av_log(NULL, AV_LOG_ERROR, "send packet to decoder failed: %s\n", av_err2str(ret));
    11. return -1;
    12. }
    13. while (ret >= 0)
    14. {
    15. // 对于frame avcodec_receive_frame 内部每次都先调用
    16. ret = avcodec_receive_frame(decoderCtx, frame);
    17. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
    18. {
    19. return;
    20. }
    21. else if (ret < 0)
    22. {
    23. av_log(NULL, AV_LOG_ERROR, "decode packet failed:%s\n", av_err2str(ret));
    24. }
    25. // 获取单个sample 占用的字节
    26. size_t dataSize = av_get_bytes_per_sample(decoderCtx->sample_fmt);
    27. if (dataSize < 0)
    28. {
    29. /*This should not occur,checking just for paranoia*/
    30. av_log(NULL, AV_LOG_ERROR, "get bytes failed!\n");
    31. return -1;
    32. }
    33. av_log(NULL, AV_LOG_INFO,"采样率: %uHZ, 通道:%u, 编码格式:%u \n", frame->sample_rate,frame->channels, frame->format);
    34. for (int i = 0; i < frame->nb_samples; i++)
    35. {
    36. for (int channel = 0; channel < decoderCtx->channels; channel++) // 交错的方式写入,float的格式输出
    37. {
    38. fwrite(frame->data[channel] + dataSize * i, 1, dataSize, dest_fp);
    39. }
    40. }
    41. }
    42. }
    1. // ffplay -ar 48000 -ac 2 -f f32le outTest.pcm
    2. int decodeAudio(const char *inFileName, const char *outFileName)
    3. {
    4. /************************************************************************************/
    5. // 打开输入文件
    6. FILE *inFile = fopen(inFileName, "rb");
    7. if (!inFile)
    8. {
    9. av_log(NULL, AV_LOG_ERROR, "Could not open:%s\n", inFileName);
    10. goto _end;
    11. }
    12. // 打开输出文件
    13. FILE *outFile = fopen(outFileName, "wb");
    14. if (!outFile)
    15. {
    16. av_log(NULL, AV_LOG_ERROR, "Could not open:%s\n", inFileName);
    17. goto _end;
    18. }
    19. uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    20. uint8_t *data = inbuf;
    21. size_t dataSize = fread(inbuf, 1, AUDIO_INBUF_SIZE, inFile);
    22. /************************************************************************************/
    23. enum AVCodecID AudioCodecID = AV_CODEC_ID_AAC;
    24. if (strstr(inFileName, "aac") != NULL)
    25. {
    26. AudioCodecID = AV_CODEC_ID_AAC;
    27. }
    28. else if (strstr(inFileName, "mp3") != NULL)
    29. {
    30. AudioCodecID = AV_CODEC_ID_MP3;
    31. }
    32. else
    33. {
    34. av_log(NULL, AV_LOG_WARNING, "default codec id:%d\n", AudioCodecID);
    35. }
    36. // 查找解码器
    37. const AVCodec *decoder;
    38. decoder = avcodec_find_decoder(AudioCodecID); // AV_CODEC_ID_AAC
    39. if (!decoder)
    40. {
    41. av_log(NULL, AV_LOG_ERROR, "decoder not found\n");
    42. goto _end;
    43. }
    44. // 获得裸流的解析器 -- 根据制定的解码器ID初始化相应裸流的解析器
    45. AVCodecParserContext *parserCtx = av_parser_init(decoder->id);
    46. if (!parserCtx)
    47. {
    48. av_log(NULL, AV_LOG_ERROR, "parserCtx not found\n");
    49. goto _end;
    50. }
    51. AVCodecContext *decoderCtx = NULL;
    52. decoderCtx = avcodec_alloc_context3(decoder);
    53. if (!decoderCtx)
    54. {
    55. av_log(NULL, AV_LOG_ERROR, "Conld not allocate audio codec context\n");
    56. goto _end;
    57. }
    58. // 将解码器和解码器上下文进行关联
    59. int ret = avcodec_open2(decoderCtx, decoder, NULL);
    60. if (ret < 0)
    61. {
    62. av_log(NULL, AV_LOG_ERROR, "Could not open codec\n");
    63. goto _end;
    64. }
    65. AVFrame *decodeFrame = av_frame_alloc();
    66. if (decodeFrame == NULL)
    67. {
    68. av_log(NULL, AV_LOG_ERROR, "Could not allocate audio decodeFrame\n");
    69. goto _end;
    70. }
    71. AVPacket *packet = NULL;
    72. packet = av_packet_alloc();
    73. while (dataSize > 0)
    74. {
    75. ret = av_parser_parse2(parserCtx, decoderCtx, &packet->data, &packet->size, data, dataSize, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
    76. if (ret < 0)
    77. {
    78. av_log(NULL, AV_LOG_ERROR, "Error while parsing\n");
    79. goto _end;
    80. }
    81. data += ret; // 跳过已经解析的数据
    82. dataSize -= ret; // 对应的缓存大小也做相应的减小
    83. if (packet->size)
    84. decodeAudioInterface(decoderCtx, packet, decodeFrame, outFile);
    85. if (dataSize < AUDIO_REFLT_THRESH) // 如果数据少了则再次读取
    86. {
    87. memmove(inbuf, data, dataSize); // 把之前剩的数据拷贝到 buffer 的其实位置
    88. data = inbuf;
    89. // 读取数据 长度: AUDIO_INBUF_SIZE - dataSize
    90. int len = fread(data + dataSize, 1, AUDIO_INBUF_SIZE - dataSize, inFile);
    91. if (len > 0)
    92. dataSize += len;
    93. }
    94. }
    95. _end:
    96. if (outFile)
    97. fclose(outFile);
    98. if (inFile)
    99. fclose(inFile);
    100. if (decoderCtx)
    101. avcodec_free_context(&decoderCtx);
    102. if (parserCtx)
    103. av_parser_close(parserCtx);
    104. if (decodeFrame)
    105. av_frame_free(&decodeFrame);
    106. if (packet)
    107. av_packet_free(&packet);
    108. return 0;
    109. }

  • 相关阅读:
    Django调用SECRET_KEY对数据进行加密
    LeetCode每日一题(1621. Number of Sets of K Non-Overlapping Line Segments)
    ruoyi-plus创建模块、自动生成代码
    【论文笔记_知识蒸馏_2021】PANets: Learning from the Universal Pixel Attention Networks
    Linux性能学习(4.6):网络_孤儿连接、半连接状态、RTS复位报文简述
    RKMEDIA使用简介
    jni头文件详解
    ESP8266-Arduino网络编程实例-HightCharts实时图表显示BME280数据
    .NET6 命令行启动及发布单个Exe文件
    upload-labs通关(Pass01-Pass05)
  • 原文地址:https://blog.csdn.net/weixin_44977283/article/details/140349472