FMPEG4.1 - 音頻解碼

FFMPEG4.1 - 音頻解碼

原文件爲採樣率44100,單聲道,fltp音頻格式的mp4文件
解碼後,將fltp轉變爲s16標準的pcm格式
然後使用ffplay播放:ffplay -ar 44100 -ac 1 -f s16le -i 2.pcm

1、下面是使用swr_convert進行轉換s16的代碼

 

// 解碼爲PCM格式文件
// 檢查解碼內容,使用如下命令:
#include <memory>

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
}

// 申請智能指針變量
#define NEW_PTR(T, P, V, Fn) T *P = V; std::shared_ptr<T> P##P(P, [&P](T *){if(P != nullptr){Fn;}})
// 打印異常信息,並退出main函數
#define FATAL(M, ...) printf(M, ##__VA_ARGS__); return -1

// 自定義變量
const char *src_media = "D:/1.mp4";
const char *dst_media = "D:/2.pcm";

// 定義要輸出的pcm格式
#define PCM_FORMAT AV_SAMPLE_FMT_S16
#define PCM_CHANNELS 1
#define PCM_SAMPLE_RATE 44100

int main(int argc, char **argv) {
    // 根據輸入文件,打開媒體格式上下文
    NEW_PTR(AVFormatContext, fmt_ctx, nullptr, avformat_close_input(&fmt_ctx));
    if (avformat_open_input(&fmt_ctx, src_media, nullptr, nullptr) < 0) {
        FATAL("open src media file failed.");
    }

    // 查詢stream信息
    if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
        FATAL("find stream info failed.");
    }

    // 查詢音頻stream的索引
    int audio_st_idx;
    if ((audio_st_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0)) < 0) {
        FATAL("find audio stream failed.");
    }

    // 通過索引找到音頻stream
    AVStream *audio_st;
    audio_st = fmt_ctx->streams[audio_st_idx];

    // 查詢音頻stream的解碼器
    AVCodec *audio_dec;
    if ((audio_dec = avcodec_find_decoder(audio_st->codecpar->codec_id)) == nullptr) {
        FATAL("find audio decoder failed.");
    }

    // 爲解碼器申請上下文
    NEW_PTR(AVCodecContext, audio_dec_ctx, nullptr, avcodec_free_context(&audio_dec_ctx));
    if ((audio_dec_ctx = avcodec_alloc_context3(audio_dec)) == nullptr) {
        FATAL("allocate audio dec context failed.");
    }

    // 從音頻stream中拷貝解碼參數到解碼器的上下文
    if (avcodec_parameters_to_context(audio_dec_ctx, audio_st->codecpar) < 0) {
        FATAL("copy codec params failed.");
    }

    // 打開解碼器
    if (avcodec_open2(audio_dec_ctx, audio_dec, nullptr) < 0) {
        FATAL("codec open failed.");
    }

    // 申請一個packet,並初始化
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = nullptr;
    pkt.size = 0;

    // 申請一個frame
    NEW_PTR(AVFrame, audio_frame, nullptr, av_frame_free(&audio_frame));
    if ((audio_frame = av_frame_alloc()) == nullptr) {
        FATAL("allocate audio frame failed.");
    }

    // 創建音頻轉換後的frame
    NEW_PTR(AVFrame, convert_frame, nullptr, av_frame_free(&convert_frame));
    if ((convert_frame = av_frame_alloc()) == nullptr) {
        FATAL("allocate convert frame failed.");
    }
    convert_frame->format = PCM_FORMAT;
    convert_frame->nb_samples = audio_dec_ctx->frame_size;
    convert_frame->channel_layout = (uint64_t)av_get_default_channel_layout(PCM_CHANNELS);
    convert_frame->sample_rate = PCM_SAMPLE_RATE;

    if(av_frame_get_buffer(convert_frame, 0) < 0) {
        FATAL("create convert frame buffer failed.");
    }

    // 創建swr的上下文
    NEW_PTR(SwrContext, swr_ctx, swr_alloc(), swr_free(&swr_ctx));
    swr_alloc_set_opts(swr_ctx,
                       convert_frame->channel_layout,
                       (AVSampleFormat)convert_frame->format,
                       convert_frame->sample_rate,
                       av_get_default_channel_layout(audio_dec_ctx->channels),
                       audio_dec_ctx->sample_fmt,
                       audio_dec_ctx->sample_rate,
                       0, nullptr);
    swr_init(swr_ctx);

    // 打開輸出文件
    NEW_PTR(FILE, output, nullptr, fclose(output));
    if ((output = fopen(dst_media, "wb")) == nullptr) {
        FATAL("no writable file.");
    }

    // 如果小於0,則表示讀完了或者報錯了,跳出循環
    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        // 我們只關心音頻stream
        if (pkt.stream_index == audio_st_idx) {
            // 發送一個音頻包
            if (avcodec_send_packet(audio_dec_ctx, &pkt) < 0) {
                FATAL("send packet exception.");
            }
            // 接受解碼完的內容
            while (true) {
                auto frame_ret = avcodec_receive_frame(audio_dec_ctx, audio_frame);
                // 判斷是否完全接受了frame
                if (frame_ret == AVERROR(EAGAIN) || frame_ret == AVERROR_EOF) {
                    break;
                }
                // 檢查是否接受異常
                if (frame_ret < 0) {
                    FATAL("receive frame exception.");
                }

                // 轉換音頻frame
                swr_convert(swr_ctx, convert_frame->data, convert_frame->nb_samples,
                            (const uint8_t **) audio_frame->data, audio_frame->nb_samples);

                // 寫入文件,如果是帶P的音頻格式,需要分通道寫入文件
                if (av_sample_fmt_is_planar((AVSampleFormat)convert_frame->format)) {
                    // 計算一個樣本的大小
                    auto sampleBytes = av_get_bytes_per_sample((AVSampleFormat)convert_frame->format);
                    for (int i = 0; i < convert_frame->nb_samples; ++i) {
                        for (int ch = 0; ch < convert_frame->channels; ++ch) {
                            fwrite(convert_frame->data[ch] + i * sampleBytes, 1, (size_t)sampleBytes, output);
                        }
                    }
                } else {
                    // 計算幀的大小
                    auto frameBytes = av_samples_get_buffer_size(nullptr, convert_frame->channels, convert_frame->nb_samples, (AVSampleFormat)convert_frame->format, 1);
                    fwrite(convert_frame->data[0], 1, (size_t)frameBytes, output);
                }

                av_frame_unref(audio_frame);
            }
        }
        av_packet_unref(&pkt);
    }

    return 0;
}

 

2、下面是使用filter進行轉換s16的代碼

// 解碼爲PCM格式文件
// 檢查解碼內容,使用如下命令:
#include <memory>

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
#include "libavutil/opt.h"
}

// 申請智能指針變量
#define NEW_PTR(T, P, V, Fn) T *P = V; std::shared_ptr<T> P##P(P, [&P](T *){if(P != nullptr){Fn;}})
// 打印異常信息,並退出main函數
#define FATAL(M, ...) printf(M, ##__VA_ARGS__); return -1

// 自定義變量
const char *src_media = "D:/1.mp4";
const char *dst_media = "D:/2.pcm";

// 定義要輸出的pcm格式
#define PCM_FORMAT AV_SAMPLE_FMT_S16
#define PCM_CHANNELS 1
#define PCM_SAMPLE_RATE 44100

int main(int argc, char **argv) {
    // 根據輸入文件,打開媒體格式上下文
    NEW_PTR(AVFormatContext, fmt_ctx, nullptr, avformat_close_input(&fmt_ctx));
    if (avformat_open_input(&fmt_ctx, src_media, nullptr, nullptr) < 0) {
        FATAL("open src media file failed.");
    }

    // 查詢stream信息
    if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
        FATAL("find stream info failed.");
    }

    // 查詢音頻stream的索引
    int audio_st_idx;
    if ((audio_st_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0)) < 0) {
        FATAL("find audio stream failed.");
    }

    // 通過索引找到音頻stream
    AVStream *audio_st;
    audio_st = fmt_ctx->streams[audio_st_idx];

    // 查詢音頻stream的解碼器
    AVCodec *audio_dec;
    if ((audio_dec = avcodec_find_decoder(audio_st->codecpar->codec_id)) == nullptr) {
        FATAL("find audio decoder failed.");
    }

    // 爲解碼器申請上下文
    NEW_PTR(AVCodecContext, audio_dec_ctx, nullptr, avcodec_free_context(&audio_dec_ctx));
    if ((audio_dec_ctx = avcodec_alloc_context3(audio_dec)) == nullptr) {
        FATAL("allocate audio dec context failed.");
    }

    // 從音頻stream中拷貝解碼參數到解碼器的上下文
    if (avcodec_parameters_to_context(audio_dec_ctx, audio_st->codecpar) < 0) {
        FATAL("copy codec params failed.");
    }

    // 打開解碼器
    if (avcodec_open2(audio_dec_ctx, audio_dec, nullptr) < 0) {
        FATAL("codec open failed.");
    }

    // 申請一個packet,並初始化
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = nullptr;
    pkt.size = 0;

    // 申請一個frame
    NEW_PTR(AVFrame, audio_frame, nullptr, av_frame_free(&audio_frame));
    if ((audio_frame = av_frame_alloc()) == nullptr) {
        FATAL("allocate audio frame failed.");
    }

    // 創建音頻轉換後的frame
    NEW_PTR(AVFrame, filter_frame, nullptr, av_frame_free(&filter_frame));
    if ((filter_frame = av_frame_alloc()) == nullptr) {
        FATAL("allocate convert frame failed.");
    }

    // 添加音頻轉換過濾器
    NEW_PTR(AVFilterGraph, filter_graph, nullptr, avfilter_graph_free(&filter_graph));
    if ((filter_graph = avfilter_graph_alloc()) == nullptr) {
        FATAL("alloc graph failed.");
    }

    char args[512] = {0};
    snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
            audio_st->time_base.num, audio_st->time_base.den,
            audio_dec_ctx->sample_rate, av_get_sample_fmt_name(audio_dec_ctx->sample_fmt),
            audio_dec_ctx->channel_layout);

    AVFilterContext *buffer_src_ctx = nullptr;
    const AVFilter *in = avfilter_get_by_name("abuffer");
    if (avfilter_graph_create_filter(&buffer_src_ctx, in, "in", args, nullptr, filter_graph)) {
        FATAL("create src filter failed.");
    }

    AVFilterContext *buffer_sink_ctx = nullptr;
    const AVFilter *out = avfilter_get_by_name("abuffersink");
    if (avfilter_graph_create_filter(&buffer_sink_ctx, out, "out", nullptr, nullptr, filter_graph)) {
        FATAL("create sink filter failed.");
    }

    enum AVSampleFormat sample_fmts[] = {PCM_FORMAT, AV_SAMPLE_FMT_NONE};
    if (av_opt_set_int_list(buffer_sink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN)) {
        FATAL("set sink filter options sample_fmts failed.");
    }

    int64_t channel_layouts[] = {av_get_default_channel_layout(PCM_CHANNELS), -1};
    if (av_opt_set_int_list(buffer_sink_ctx, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN)) {
        FATAL("set sink filter options channel_layouts failed.");
    }

    int sample_rates[] = {PCM_SAMPLE_RATE, -1};
    if (av_opt_set_int_list(buffer_sink_ctx, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN)) {
        FATAL("set sink filter options sample_rates failed.");
    }

    NEW_PTR(AVFilterInOut, inputs, nullptr, avfilter_inout_free(&inputs));
    if ((inputs = avfilter_inout_alloc()) == nullptr) {
        FATAL("alloc inputs failed.");
    }

    inputs->name = av_strdup("out");
    inputs->filter_ctx = buffer_sink_ctx;
    inputs->pad_idx = 0;
    inputs->next = nullptr;

    NEW_PTR(AVFilterInOut, outputs, nullptr, avfilter_inout_free(&outputs));
    if ((outputs = avfilter_inout_alloc()) == nullptr) {
        FATAL("alloc outputs failed.");
    }

    outputs->name = av_strdup("in");
    outputs->filter_ctx = buffer_src_ctx;
    outputs->pad_idx = 0;
    outputs->next = nullptr;

    char channel_layout_str[100] = {0};
    av_get_channel_layout_string(channel_layout_str, sizeof(channel_layout_str), PCM_CHANNELS, (uint64_t)av_get_default_channel_layout(PCM_CHANNELS));

    char filter_desc[512] = {0};
    snprintf(filter_desc, sizeof(filter_desc), "aresample=%d,aformat=sample_fmts=%s:channel_layouts=%s",
             PCM_SAMPLE_RATE, av_get_sample_fmt_name(PCM_FORMAT), channel_layout_str);

    if (avfilter_graph_parse_ptr(filter_graph, filter_desc, &inputs, &outputs, nullptr) < 0) {
        FATAL("parse graph failed.");
    }

    if (avfilter_graph_config(filter_graph, nullptr) < 0) {
        FATAL("config graph failed.");
    }

    // 打開輸出文件
    NEW_PTR(FILE, output, nullptr, fclose(output));
    if ((output = fopen(dst_media, "wb")) == nullptr) {
        FATAL("no writable file.");
    }

    // 如果小於0,則表示讀完了或者報錯了,跳出循環
    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        // 我們只關心音頻stream
        if (pkt.stream_index == audio_st_idx) {
            // 發送一個音頻包
            if (avcodec_send_packet(audio_dec_ctx, &pkt) < 0) {
                FATAL("send packet exception.");
            }
            // 接受解碼完的內容
            while (true) {
                auto frame_ret = avcodec_receive_frame(audio_dec_ctx, audio_frame);
                // 判斷是否完全接受了frame
                if (frame_ret == AVERROR(EAGAIN) || frame_ret == AVERROR_EOF) {
                    break;
                }
                // 檢查是否接受異常
                if (frame_ret < 0) {
                    FATAL("receive frame exception.");
                }

                if (av_buffersrc_add_frame_flags(buffer_src_ctx, audio_frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) {
                    break;
                }

                while (true) {
                    auto got_ret = av_buffersink_get_frame(buffer_sink_ctx, filter_frame);

                    if (got_ret == AVERROR(EAGAIN) || got_ret == AVERROR_EOF) break;

                    if (got_ret < 0) {
                        FATAL("get filter frame failed.");
                    }

                    // 寫入文件,如果是帶P的音頻格式,需要分通道寫入文件
                    if (av_sample_fmt_is_planar((AVSampleFormat)filter_frame->format)) {
                        // 計算一個樣本的大小
                        auto sampleBytes = av_get_bytes_per_sample((AVSampleFormat)filter_frame->format);
                        for (int i = 0; i < filter_frame->nb_samples; ++i) {
                            for (int ch = 0; ch < filter_frame->channels; ++ch) {
                                fwrite(filter_frame->data[ch] + i * sampleBytes, 1, (size_t)sampleBytes, output);
                            }
                        }
                    } else {
                        // 計算幀的大小
                        auto frameBytes = av_samples_get_buffer_size(nullptr, filter_frame->channels, filter_frame->nb_samples, (AVSampleFormat)filter_frame->format, 1);
                        fwrite(filter_frame->data[0], 1, (size_t)frameBytes, output);
                    }

                    av_frame_unref(filter_frame);
                }

                av_frame_unref(audio_frame);
            }
        }
        av_packet_unref(&pkt);
    }

    return 0;
}

 

 

發佈了2 篇原創文章 · 獲贊 3 · 訪問量 1764
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章