• FFmpeg 音频解码(秒懂)


    1.简介

    解码音频数据,如下图所示,把MP3或者AAC数据解码成原始的数据pcm。

     2.流程

     2.1在使用FFmpeg API之前,需要先注册API,然后才能使用API。当然,新版本的库不需要再调用下面的方法。

    av_register_all()

    2.2 构建输入AVFormatContext声明输入的封装结构体,通过输入文件或者流地址作为封装结构的句柄。

    1. AVFormatContext* ifmt_ctx = NULL;
    2. const char* inputUrl = "test.mp4";
    3. ///打开输入的流
    4. int ret = avformat_open_input(&ifmt_ctx, inputUrl, NULL, NULL);
    5. if (ret != 0)
    6. {
    7. printf("Couldn't open input stream.\n");
    8. return -1;
    9. }

    2.3查找音频流信息,通过下面的接口与AVFormatContext中建立输入文件对应的流信息。

    1. //查找;
    2. if (avformat_find_stream_info(inputFmtCtx, NULL) < 0)
    3. {
    4. printf("Couldn't find stream information.\n");
    5. return -1;
    6. }

    2.4查找解码器

    先找到音频流索引,找到音频流,根据音频流的codec_id找到解码器。

    1. //找到音频流索引
    2. int audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    3. AVStream* st = ifmt_ctx->streams[audio_index];
    4. AVCodec* codec = nullptr;
    5. //找到解码器
    6. codec = avcodec_find_decoder(st->codecpar->codec_id);
    7. if (!codec)
    8. {
    9. fprintf(stderr, "Codec not found\n");
    10. exit(1);
    11. }

    2.5申请AVCodecContenxt

    1. //申请AVCodecContext
    2. AVCodecContext* codec_ctx = nullptr;
    3. codec_ctx = avcodec_alloc_context3(codec);
    4. if (!codec_ctx)
    5. {
    6. exit(1);
    7. }

    2.6同步AVCodecParameters

    avcodec_parameters_to_context(codec_ctx, ifmt_ctx->streams[audio_index]->codecpar);

    2.7打开解码器

    1. //打开解码器
    2. if ((ret = avcodec_open2(codec_ctx, codec, NULL) < 0))
    3. {
    4. return -1;
    5. }

    2.8然后通过while循环,不停的读取数据,解码。

    1. av_read_frame(ifmt_ctx, pkt)
    2. avcodec_send_packet(codec_ctx, pkt);
    3. avcodec_receive_frame(codec_ctx, frame);

    3.源码

    演示输入一个flv文件,保存解码后的pcm数据。

    1. #include "pch.h"
    2. #include
    3. extern "C"
    4. {
    5. #include "libavformat/avformat.h"
    6. #include "libavutil/dict.h"
    7. #include "libavutil/opt.h"
    8. #include "libavutil/timestamp.h"
    9. #include "libswscale/swscale.h"
    10. #include "libswresample/swresample.h"
    11. #include "libavutil/imgutils.h"
    12. };
    13. int main()
    14. {
    15. //av_register_all();
    16. avformat_network_init();
    17. AVFormatContext* ifmt_ctx = NULL;
    18. const char* inputUrl = "out.flv";
    19. ///打开输入的流
    20. int ret = avformat_open_input(&ifmt_ctx, inputUrl, NULL, NULL);
    21. if (ret != 0)
    22. {
    23. printf("Couldn't open input stream.\n");
    24. return -1;
    25. }
    26. //查找流信息
    27. if (avformat_find_stream_info(ifmt_ctx, NULL) < 0)
    28. {
    29. printf("Couldn't find stream information.\n");
    30. return -1;
    31. }
    32. //找到音频流索引
    33. int audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    34. AVStream* st = ifmt_ctx->streams[audio_index];
    35. AVCodec* codec = nullptr;
    36. //找到解码器
    37. codec = avcodec_find_decoder(st->codecpar->codec_id);
    38. if (!codec)
    39. {
    40. fprintf(stderr, "Codec not found\n");
    41. exit(1);
    42. }
    43. //申请AVCodecContext
    44. AVCodecContext* codec_ctx = nullptr;
    45. codec_ctx = avcodec_alloc_context3(codec);
    46. if (!codec_ctx)
    47. {
    48. exit(1);
    49. }
    50. avcodec_parameters_to_context(codec_ctx, ifmt_ctx->streams[audio_index]->codecpar);
    51. //打开解码器
    52. if ((ret = avcodec_open2(codec_ctx, codec, NULL) < 0))
    53. {
    54. return -1;
    55. }
    56. AVPacket* pkt = av_packet_alloc();
    57. //av_init_packet(pkt);
    58. AVFrame *frame = av_frame_alloc();
    59. char fileName[20] = "test.pcm";
    60. FILE* f;
    61. f = fopen(fileName, "wb");
    62. while (av_read_frame(ifmt_ctx, pkt) >= 0)
    63. {
    64. if (pkt->stream_index == audio_index)
    65. {
    66. int ret = avcodec_send_packet(codec_ctx, pkt);
    67. if (ret >= 0)
    68. {
    69. ret = avcodec_receive_frame(codec_ctx, frame);
    70. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
    71. {
    72. break;
    73. }
    74. else if (ret < 0)
    75. {
    76. break;
    77. }
    78. int data_size = av_get_bytes_per_sample(codec_ctx->sample_fmt);
    79. if (data_size < 0) {
    80. continue;
    81. }
    82. for (int i = 0; i < frame->nb_samples; i++)
    83. {
    84. for (int ch = 0; ch < codec_ctx->channels; ch++)
    85. {
    86. fwrite(frame->data[ch] + data_size * i, 1, data_size, f);
    87. }
    88. }
    89. }
    90. }
    91. }
    92. fclose(f);
    93. avcodec_close(codec_ctx);
    94. avcodec_free_context(&codec_ctx);
    95. avformat_close_input(&ifmt_ctx);
    96. av_frame_free(&frame);
    97. av_packet_free(&pkt);
    98. return 0;
    99. }

    4.pcm数据工具,用于播放pcm文件

    pcm工具pcm工具pcm工具-C++文档类资源-CSDN下载

    5.查看解码前的音频数据

    可以看见解码前 :采样率是48000HZ,双声道,fltp格式。

    使用pcm工具播放 保存好的pcm文件。

     

    选择导入原始数据,设置参数跟上面一样,点击播放就行了,如果数据正确,跟解码前听到的音频是一致的。

    6.一些命令使用

    6.1从视频文件中分离出MP3文件

    ffmpeg -i out.flv -acodec libmp3lame output.mp3

    6.2查看文件信息

    ffprobe.exe -i out.flv

  • 相关阅读:
    【Git进阶】基于文件(夹)拆分大PR
    基于安卓的电力设备智能巡检APP设计
    内部项目管理方案
    conda创建python虚拟环境
    基于 vite2 + Vue3 写一个在线帮助文档工具
    【微服务~Nacos】Nacos之配置中心
    [python 刷题] 12 Integer to Roman & 13 Roman to Integer
    Nodejs和ES6的模块化 import ,export
    XPS测试加测轨道-科学指南针
    写csv相关操作
  • 原文地址:https://blog.csdn.net/wzz953200463/article/details/125900359