window下ffmpeg开发环境搭建,并将音频数据生成波形图流程

1、安装VS2019(自行下载,这里不再赘述)
2、下载ffmpeg开发包
打开ffmpeg官网:http://ffmpeg.org/
点击下载
在这里插入图片描述
选择Windows版本下载
在这里插入图片描述
选择release版本下载
在这里插入图片描述
即下载开发包地址:https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared.7z
解压包即可得到依赖库和头文件:
在这里插入图片描述
3、开发环境搭建
创建目录 如下:
在这里插入图片描述 bin、include、lib从下载的ffmpeg开发包中拷贝过来,src为自己代码路径,CMakelist.txt为编译脚本 如下:
在这里插入图片描述
4、VS2019配置
打开VS选择代码CMakelist.txt所在路径
在这里插入图片描述
在这里插入图片描述
打开后如下图所示,点击Cmakelist.txt,运行当前Cmakelist.txt即可
在这里插入图片描述
在这里插入图片描述
这样就可以调试编码了

下面是一个demo,将音频数据的波形输出成一张图片,代码如下:
audio_filer.h

#include <iostream>

extern "C"
{
#include <libavcodec/avcodec.h>//处理原始音频和视频流的解码
#include <libavformat/avformat.h>//处理解析视频文件并将包含在其中的流分离出来
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
}

class AudioFilter
{
public:
    bool Init(AVStream* stream,const std::string command);
    void Run(AVFrame* avframe, AVFrame*& filt_frame);
    ~AudioFilter();
private:
    AVFilterGraph* filter_graph = nullptr;
    AVFilterContext* buffersrc_ctx = nullptr;
    AVFilterContext* buffersink_ctx = nullptr;
    const AVFilter* buffersrc = nullptr;
    const AVFilter* buffersink = nullptr;
    AVFilterInOut* outputs = nullptr;
    AVFilterInOut* inputs = nullptr;
};

audio_filer.cpp

#include "audio_filer.h"

bool AudioFilter::Init(AVStream* stream, const std::string command)
{
    buffersrc = avfilter_get_by_name("abuffer");
    buffersink = avfilter_get_by_name("buffersink");
    filter_graph = avfilter_graph_alloc();

    char args[512];
    snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
        stream->codec->time_base.num, stream->codec->time_base.den, stream->codec->sample_rate,
        av_get_sample_fmt_name((enum AVSampleFormat)stream->codecpar->format), stream->codec->channel_layout);

    try
    {
        int ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph);
        if (ret < 0)
        {
            printf("Cannot create buffer source\n");
            throw ret;
        }

        ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, filter_graph);
        if (ret < 0)
        {
            std::cout << "Cannot create buffer sink: " << ret << std::endl;
            throw ret;
        }

        outputs = avfilter_inout_alloc();
        inputs = avfilter_inout_alloc();

        outputs->name = av_strdup("in");
        outputs->filter_ctx = buffersrc_ctx;
        outputs->pad_idx = 0;
        outputs->next = NULL;

        inputs->name = av_strdup("out");
        inputs->filter_ctx = buffersink_ctx;
        inputs->pad_idx = 0;
        inputs->next = NULL;

        if ((ret = avfilter_graph_parse_ptr(filter_graph, command.c_str(), &inputs, &outputs, NULL)) < 0)
            throw ret;

        if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
            throw ret;
    }
    catch (int e)
    {
        if (inputs != nullptr)
        {
            avfilter_inout_free(&inputs);
            inputs = nullptr;
        }
            
        if (outputs != nullptr)
        {
            avfilter_inout_free(&outputs);
            outputs = nullptr;
        }
            
        if (filter_graph != nullptr)
        {
            avfilter_graph_free(&filter_graph);
            filter_graph = nullptr;
        }
            
        return false;
    }
    
    return true;
}

void AudioFilter::Run(AVFrame* avframe, AVFrame*& filt_frame)
{
    int ret = av_buffersrc_add_frame_flags(buffersrc_ctx, avframe, 0);
    if (ret < 0)
    {
        std::cout << "Error while feeding the filtergraph\n" << std::endl;
        return ;
    }

    ret = av_buffersink_get_frame(buffersink_ctx, filt_frame);
    if (ret < 0)
    {
        av_frame_free(&filt_frame);
        filt_frame = nullptr;
        return ;
    }

}

AudioFilter::~AudioFilter()
{
    if (inputs != nullptr)
    {
        avfilter_inout_free(&inputs);
        inputs = nullptr;
    }

    if (outputs != nullptr)
    {
        avfilter_inout_free(&outputs);
        outputs = nullptr;
    }

    if (filter_graph != nullptr)
    {
        avfilter_graph_free(&filter_graph);
        filter_graph = nullptr;
    }
}

image.h

#include <iostream>
extern "C"
{
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    #include <libavfilter/buffersink.h>
    #include <libavfilter/buffersrc.h>
    #include <libswscale/swscale.h>
}

class Image
{
public:
    bool change_pix_format(AVFrame* src_avframe, AVFrame*& dst_avframe, AVPixelFormat format);
    void ProcessFrame(std::string output, AVFrame* frame);
};


image.cpp

#include "image.h"
#include <iostream>

bool Image::change_pix_format(AVFrame* src_avframe, AVFrame*& dst_avframe, AVPixelFormat format)
{
    if (src_avframe->format == format)
    {
        AVFrame* temp = av_frame_clone(src_avframe);
        dst_avframe = temp;
        return true;
    }
    AVFrame* dec_avframe = av_frame_alloc();
    dec_avframe->format = format;
    dec_avframe->width = src_avframe->width;
    dec_avframe->height = src_avframe->height;

    int ret = 0;
    if ((ret = av_frame_get_buffer(dec_avframe, 0)) < 0) {
        return false;
    }

    struct SwsContext* swsContext = sws_getContext(src_avframe->width, src_avframe->height, (AVPixelFormat)src_avframe->format, dec_avframe->width, dec_avframe->height, format, SWS_BICUBIC, NULL, NULL, NULL);

    sws_scale(swsContext, src_avframe->data, src_avframe->linesize, 0, src_avframe->height, dec_avframe->data, dec_avframe->linesize);

    av_frame_copy_props(dec_avframe, src_avframe);
    dst_avframe = dec_avframe;
    sws_freeContext(swsContext);
}

void Image::ProcessFrame(std::string output,AVFrame * frame)
{
    AVFormatContext* output_ctx = nullptr;

    AVCodecContext * codec_ctx = nullptr;
    try
    {
        if (int ret = avformat_alloc_output_context2(&output_ctx, nullptr, nullptr, output.c_str()) < 0)
        {
            std::cout << "avformat_alloc_output_context2 error! url: " << output << " ret: " << ret << std::endl;
            throw ret;
        }
        //获取封装格式
        AVOutputFormat* fmt = av_guess_format(nullptr, output.c_str(), nullptr);
        if (fmt)
            output_ctx->oformat = fmt;

        //根据输入的解码器类型获取输出封装格式对应的编码器id
        AVCodecID codecId = av_guess_codec(output_ctx->oformat, nullptr, output.c_str(), nullptr, AVMEDIA_TYPE_VIDEO);

        //根据编码器id获取编码器
        AVCodec* codec = avcodec_find_encoder(codecId);
        if (codec == nullptr)
        {
            std::cout << "find encodec is null! " << std::endl;
            throw -1;
        }
        //初始化编码器上下文参数
        AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
        if (codec_ctx == nullptr)
        {
            std::cout << "codec_ctx is null!" << std::endl;
            throw - 2;
        }
        AVFrame* frame_temp = nullptr;
        if (frame->format!= codec->pix_fmts[0])
        {

            if(change_pix_format(frame,frame_temp, codec->pix_fmts[0]))
            {
                av_frame_free(&frame);
                frame = frame_temp;
            }
        }

        //配置编码器
        codec_ctx->codec_id = codecId;
        codec_ctx->pix_fmt = codec->pix_fmts[0];
        codec_ctx->width = frame->width;
        codec_ctx->height = frame->height;
        codec_ctx->time_base = { 1,25 };

         
        //判断输出的封装格式是否有编解码器参数信息,没有则添加上
        if (output_ctx->oformat->flags & AVFMT_GLOBALHEADER)
        {
            codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
        }

        //打开编码器
        int ret = avcodec_open2(codec_ctx, codec, nullptr);
        if (ret < 0)
        {
            std::cout << "open encodec is error!" << std::endl;
            throw ret;
        }

        //初始化输出流
        AVStream* output_stream = avformat_new_stream(output_ctx, nullptr);
        if (int ret = avcodec_parameters_from_context(output_stream->codecpar, codec_ctx) < 0)
        {
            std::cout << "avcodec_parameters_from_context error! ret: " << ret << std::endl;
            throw - 2;
        }

        //打开输出流
        if (!(output_ctx->flags & AVFMT_NOFILE))//是文件才打开输出流
        {
            if (int ret = avio_open2(&output_ctx->pb, output.c_str(), AVIO_FLAG_WRITE, nullptr, nullptr) < 0)
            {
                std::cout << "avio_open2 error! ret: " << ret << std::endl;
                throw - 2;
            }
        }

        // 写输出文件头
        if (int ret = avformat_write_header(output_ctx, nullptr) < 0)
        {
            std::cout << "avformat_write_header error! ret: " << ret << std::endl;
            throw - 2;
        }

        ret = avcodec_send_frame(codec_ctx, frame);
        if (ret < 0)
        {
            std::cout << "send frame error! ret:" << ret << std::endl;
            throw ret;
        }

        ret = avcodec_send_frame(codec_ctx, nullptr);
        if (ret < 0)
        {
            std::cout << "send frame error! ret:" << ret << std::endl;
            throw ret;
        }

        while (1)
        {
            AVPacket new_packet;
            av_init_packet(&new_packet);
            new_packet.data = nullptr;
            new_packet.size = 0;
            //从编码器中获取编码后的数据包
            ret = avcodec_receive_packet(codec_ctx, &new_packet);
            if (ret < 0)
                throw ret;

            //将数据包写入到打开的输出源中
            ret = av_interleaved_write_frame(output_ctx, &new_packet);//写文件
            if (ret < 0)
            {
                std::cout << "write packet error! ret:" << ret << std::endl;
                throw ret;
            }
            av_packet_unref(&new_packet);
        }
        av_frame_free(&frame);
    }
    catch (int e)
    {
        std::cout << "Transcoding exception: " << e << std::endl;
 
        //写文件尾
        av_write_trailer(output_ctx);

        //打印输出源信息
        av_dump_format(output_ctx, 0, output.c_str(), 1);
        if (output_ctx != nullptr)
        {
            if (!(output_ctx->flags & AVFMT_NOFILE))
            {
                avio_closep(&output_ctx->pb);
            }
            avformat_free_context(output_ctx);
            output_ctx = nullptr;
        }

        if (codec_ctx != nullptr)
        {
            avcodec_free_context(&codec_ctx);
            codec_ctx = nullptr;
        }
    }
}

input.h

#include <iostream>
#include <map>
#include "audio_filer.h"
#include <functional>

extern "C"
{
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    #include <libavfilter/buffersink.h>
    #include <libavfilter/buffersrc.h>
}

class Input
{
public:
    bool Init(std::string path);
    void Run(std::function<void(AVFrame* frame)> call_back);
    ~Input();
public:
    AVFormatContext* input_ctx = nullptr;
    std::map<int, AVCodecContext*> input_codec_ctx_map;
    int video_streamIndex = -1;
    int audio_streamIndex = -1;
    AudioFilter* audio_filter = nullptr;
};

input.cpp

#include "input.h"

bool Input::Init(std::string path)
{
    try
    {
        if (int ret = avformat_open_input(&input_ctx, path.c_str(), NULL, NULL) < 0)
        {
            std::cout << "avformat_open_input error! url:" << path << " ret:" << ret << std::endl;
            throw ret;
        }

        //获取流信息
        if (int ret = avformat_find_stream_info(input_ctx, NULL) < 0)
        {
            std::cout << "avformat_find_stream_info failed! ret: " << ret << std::endl;
            throw ret;
        }
        //打印输入源信息
        av_dump_format(input_ctx, 0, path.c_str(), 0);

        /*****************************配置解码器参数**********************************/
        for (int i = 0; i < input_ctx->nb_streams; i++)
        {
            AVStream* stream = input_ctx->streams[i];
            //根据流信息中的解码id找到解码器
            AVCodec* codec = avcodec_find_decoder(stream->codecpar->codec_id);
            if (codec == nullptr)
            {
                std::cout << "find decodec is null!" << std::endl;
                continue;
            }
            //使用解码器对象初始化解码器上下文参数
            AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
            if (codec_ctx == nullptr)
            {
                std::cout << "codec_ctx is null!" << std::endl;
                throw -1;
            }
            //视频流中解码器参数来初始化解码器上下文成员
            int ret = avcodec_parameters_to_context(codec_ctx, stream->codecpar);
            if (ret < 0)
            {
                std::cout << "init codec_ctx parameters is error!" << std::endl;
                throw ret;
            }

            //打开解码器
            ret = avcodec_open2(codec_ctx, codec, nullptr);
            if (ret < 0)
            {
                std::cout << "open decodec error!" << std::endl;
                throw ret;
            }

            //获取音视频流的索引id
            if (codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                audio_streamIndex = i;
                if (audio_filter == nullptr)
                {
                    audio_filter = new AudioFilter();
                    std::string command = "[in]showwavespic=s=720x180[out]";
                    audio_filter->Init(stream, command);
                }

            }
            if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                video_streamIndex = i;
            }
            input_codec_ctx_map.insert(std::make_pair(i, codec_ctx));
        }
        /*****************************配置解码器参数**********************************/
    }
    catch (int e)
    {
        std::cout << "Init exception: " << e << std::endl;
        if (input_ctx != nullptr)
        {
            avformat_close_input(&input_ctx);
            input_ctx = nullptr;
        }
        for (auto& it : input_codec_ctx_map)
        {
            avcodec_free_context(&it.second);
        }
        input_codec_ctx_map.clear();
        return false;
    }
    return true;
}

void Input::Run(std::function<void(AVFrame* frame)> call_back)
{
    //初始化一个AVPacket来获取数据包
    AVPacket packet;
    av_init_packet(&packet);
    packet.data = nullptr;
    packet.size = 0;
    //初始化一个解码后的帧
    AVFrame* frame = av_frame_alloc();

    //从源上下文中读取数据包
    while (av_read_frame(input_ctx, &packet) >= 0)
    {
        if (packet.stream_index != audio_streamIndex)
        {
            av_packet_unref(&packet);
            continue;
        }

        if (input_codec_ctx_map.end() == input_codec_ctx_map.find(packet.stream_index))
        {
            av_packet_unref(&packet);
            continue;
        }

        //将读取到的音视频包送到解码器解码
        int ret = avcodec_send_packet(input_codec_ctx_map.at(packet.stream_index), &packet);
        if (ret < 0)
        {
            std::cout << "send packet error! ret:" << ret << std::endl;
            av_packet_unref(&packet);
            continue;
        }

        while (1)
        {
            //从解码器中获取解码后的音视频数据
            int ret = avcodec_receive_frame(input_codec_ctx_map.at(packet.stream_index), frame);
            if (ret < 0)
            {
                break;
            }
            AVFrame* filter = av_frame_alloc();
            audio_filter->Run(frame, filter);
            if (filter != nullptr)
            {
                av_frame_free(&filter);
            }
        }
        av_packet_unref(&packet);
    }
    AVFrame* filter = av_frame_alloc();
    audio_filter->Run(nullptr, filter);
    if (filter != nullptr)
    {
       call_back(filter);
    }
}

Input::~Input()
{
    if (input_ctx != nullptr)
    {
        avformat_close_input(&input_ctx);
        input_ctx = nullptr;
    }
    for (auto& it : input_codec_ctx_map)
    {
        avcodec_free_context(&it.second);
    }
    input_codec_ctx_map.clear();
}

main.cpp

#include <iostream>
#include "input.h"
#include "image.h"


int main()
{
    std::cout << "learn ffmpeg" << std::endl;
    std::string input_path = "D:\\dashuangtest\\tuiliu.mp4";
    std::string output_path = "D:\\dashuangtest\\tuiliu.jpg";
    Input input;
    input.Init(input_path);
    input.Run([&](AVFrame* frame)
        {
            Image image;
            image.ProcessFrame(output_path,frame);
        
        });

    getchar();
	return 0;
}

主要流程是将媒体文件的音频流获取到,解码出来,使用showwavespic的滤镜生成图片
ffmpeg的filter使用参考:https://ffmpeg.org/ffmpeg-filters.html#showwavespic
在这里插入图片描述
效果图如下:
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
敬告:该系列的课程在抓紧录制更新中,敬请大家关注。敬告: 该系列的课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等。包括:音视频、流媒体、直播、Android、视频监控28181、等。  我将带领大家一起来学习:Qt麦克风数据探测、FFmpeg解码音频数据音频波形图音频播放器。具体内容包括:1.Qt采集麦克风并探测数据生成动态矩形图。2.FFMpeg解码音频数据生成PCM数据,并绘制波形图。3.音频播放器实战。 音视频与流媒体是一门很复杂的技术,涉及的概念、原理、理论非常多,很多初学者不学 基础理论,而是直接做项目,往往会看到c/c++代码时一头雾水,不知道代码到底是什么意思,这是为什么呢? 因为没有学习音视频和流媒体的基础理论,就比如学习英语,不学习基本单词,而是天天听英语新闻,总也听不懂。所以呢,一定要认真学习基础理论,然后再学习播放器、转码器、非编、流媒体直播、视频监控、等等。 梅老师从事音视频与流媒体行业18年;曾在永新视博、中科大洋、百度、美国Harris广播事业部等公司就职,经验丰富;曾亲手主导广电直播全套项目,精通h.264/h.265/aac,曾亲自参与百度app上的网页播放器等实战产品。目前全身心自主创业,主要聚焦音视频+流媒体行业,精通音视频加密、流媒体在线转码快编等热门产品。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值