暗黑程序员 2019-10-10 15:02 采纳率: 0%
浏览 1496
已结题

FFmpeg - 为什么从pcm提取音频,编码为mp3以后,时长变多了,声音也颤抖

#include <memory>

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/avassert.h"
#include "libavutil/audio_fifo.h"
}

// 函数退出后执行
#define DEFER(T, P, Fn) std::shared_ptr<T> P##deletor(P, [&](T *) {Fn;})
// 打印异常信息,并退出main函数
#define FATAL(M, ...) printf(M, ##__VA_ARGS__); return -1

// 自定义变量
const char *src_media = "E:/2.pcm";
const char *dst_media = "E:/3.mp3";

// PCM的原始参数
#define PCM_IN_FORMAT AV_SAMPLE_FMT_S16
#define PCM_IN_CHANNEL_LAYOUT AV_CH_LAYOUT_MONO
#define PCM_IN_NB_SAMPLES 1024
#define PCM_IN_SAMPLE_RATE 44100

int main(int argc, char **argv) {
    // 申请一个输出的上下文
    AVFormatContext *fmt_ctx = nullptr;
    avformat_alloc_output_context2(&fmt_ctx, nullptr, nullptr, dst_media);
    if (fmt_ctx == nullptr) {
        FATAL("alloc output format context failed.");
    }
    DEFER(AVFormatContext, fmt_ctx, avio_closep(&fmt_ctx->pb);
            avformat_close_input(&fmt_ctx));

    // 查询编码器
    AVCodec *audio_enc;
    if ((audio_enc = avcodec_find_encoder(fmt_ctx->oformat->audio_codec)) == nullptr) {
        FATAL("find audio encoder failed.");
    }

    AVStream *audio_stream = avformat_new_stream(fmt_ctx, audio_enc);
    audio_stream->id = fmt_ctx->nb_streams - 1;

    // 为编码器申请上下文
    AVCodecContext *audio_enc_ctx = nullptr;
    if ((audio_enc_ctx = avcodec_alloc_context3(audio_enc)) == nullptr) {
        FATAL("allocate audio enc context failed.");
    }
    DEFER(AVCodecContext, audio_enc_ctx, avcodec_free_context(&audio_enc_ctx));

    // 为编码器配置编码参数
    audio_enc_ctx->sample_fmt = audio_enc->sample_fmts ? audio_enc->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
    audio_enc_ctx->bit_rate = 128000;
    audio_enc_ctx->sample_rate = 44100;
    audio_enc_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
    audio_enc_ctx->channels = av_get_channel_layout_nb_channels(audio_enc_ctx->channel_layout);

    if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
        audio_enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

    audio_stream->time_base = AVRational{1, audio_enc_ctx->sample_rate};

    // 打开编码器
    if (avcodec_open2(audio_enc_ctx, audio_enc, nullptr) < 0) {
        FATAL("codec open failed.");
    }

    if (avcodec_parameters_from_context(audio_stream->codecpar, audio_enc_ctx) < 0) {
        FATAL("copy params failed.");
    }

    if (avio_open(&fmt_ctx->pb, dst_media, AVIO_FLAG_WRITE) < 0) {
        FATAL("open dst file failed.");
    }

    // 写入头信息
    if (avformat_write_header(fmt_ctx, nullptr) < 0) {
        FATAL("write header failed.");
    }

    // 申请一个音频frame
    AVFrame *audio_frame = nullptr;
    if ((audio_frame = av_frame_alloc()) == nullptr) {
        FATAL("allocate frame failed.");
    }
    DEFER(AVFrame, audio_frame, av_frame_free(&audio_frame));

    audio_frame->format = audio_enc_ctx->sample_fmt;
    audio_frame->channel_layout = audio_enc_ctx->channel_layout;
    audio_frame->nb_samples = audio_enc_ctx->frame_size;
    audio_frame->sample_rate = audio_enc_ctx->sample_rate;

    // 给frame的data和size分配空间
    if (av_frame_get_buffer(audio_frame, 0) < 0) {
        FATAL("allocate frame data failed.");
    }

    // 创建swr的上下文
    SwrContext *swr_cxt = swr_alloc();
    DEFER(SwrContext, swr_cxt, swr_free(&swr_cxt));
    // 获取输入与输出的采样格式
    enum AVSampleFormat in_sample_fmt = PCM_IN_FORMAT;
    enum AVSampleFormat out_sample_fmt = audio_enc_ctx->sample_fmt;
    // 获取输入与输出的采样率
    int in_sample_rate = PCM_IN_SAMPLE_RATE;
    int out_sample_rate = audio_enc_ctx->sample_rate;
    // 获取输入与输出的声道格式
    uint64_t in_ch_layout = PCM_IN_CHANNEL_LAYOUT;
    uint64_t out_ch_layout = audio_enc_ctx->channel_layout;
    // 设置swr的配置项
    swr_alloc_set_opts(swr_cxt, out_ch_layout, out_sample_fmt, out_sample_rate,
                       in_ch_layout, in_sample_fmt, in_sample_rate, 0, nullptr);
    // 初始化swr
    swr_init(swr_cxt);

    // 申请一个packet,并初始化
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = nullptr;
    pkt.size = 0;

    FILE *input = nullptr;
    if ((input = fopen(src_media, "rb")) == nullptr) {
        FATAL("no readable file.");
    }
    DEFER(FILE, input, fclose(input));

    // 从文件中读取出来的数据,临时存储在该帧中
    AVFrame *temp_frame = av_frame_alloc();
    DEFER(AVFrame, temp_frame, av_frame_free(&temp_frame));

    temp_frame->format = PCM_IN_FORMAT;
    temp_frame->nb_samples = PCM_IN_NB_SAMPLES;
    temp_frame->channel_layout = PCM_IN_CHANNEL_LAYOUT;
    temp_frame->channels = av_get_channel_layout_nb_channels(PCM_IN_CHANNEL_LAYOUT);

    // 给frame的data和size分配空间
    if (av_frame_get_buffer(temp_frame, 0) < 0) {
        FATAL("allocate frame data failed.");
    }

    // 循环读取frame数据
    int frame_count = 0;
    while (true) {
        if (av_frame_make_writable(audio_frame) < 0) {
            FATAL("frame is not writable");
        }

        if (fread(temp_frame->data[0], 1, (size_t) temp_frame->linesize[0], input) < 0) {
            FATAL("read input file failed.");
        } else if (feof(input)) {
            break;
        }

        swr_convert(swr_cxt, audio_frame->data, audio_frame->nb_samples, (const uint8_t **) temp_frame->data, temp_frame->nb_samples);

        audio_frame->pts = av_rescale_q(frame_count, (AVRational){1, audio_enc_ctx->sample_rate}, audio_enc_ctx->time_base);
        frame_count += audio_frame->nb_samples;

        // 发送一个frame
        if (avcodec_send_frame(audio_enc_ctx, audio_frame) < 0) {
            FATAL("send frame exception.");
        }
        // 接受编码完的内容
        while (true) {
            auto packet_ret = avcodec_receive_packet(audio_enc_ctx, &pkt);
            // 判断是否完全接受了packet
            if (packet_ret == AVERROR(EAGAIN) || packet_ret == AVERROR_EOF) {
                break;
            }
            // 检查是否接受异常
            if (packet_ret < 0) {
                FATAL("receive packet exception.");
            }

            av_packet_rescale_ts(&pkt, audio_enc_ctx->time_base, audio_stream->time_base);
            pkt.stream_index = audio_stream->index;

            av_interleaved_write_frame(fmt_ctx, &pkt);

            av_packet_unref(&pkt);
        }
    }

    // 发送一个空的frame,告知编码器,已经编码完成,清空缓冲区
    if (avcodec_send_frame(audio_enc_ctx, nullptr) < 0) {
        FATAL("send frame exception.");
    }
    // 接受编码完的内容
    while (true) {
        auto packet_ret = avcodec_receive_packet(audio_enc_ctx, &pkt);
        // 判断是否完全接受了packet
        if (packet_ret == AVERROR(EAGAIN) || packet_ret == AVERROR_EOF) {
            break;
        }
        // 检查是否接受异常
        if (packet_ret < 0) {
            FATAL("receive packet exception.");
        }

        av_packet_rescale_ts(&pkt, audio_enc_ctx->time_base, audio_stream->time_base);
        pkt.stream_index = audio_stream->index;

        av_interleaved_write_frame(fmt_ctx, &pkt);

        av_packet_unref(&pkt);
    }

    av_write_trailer(fmt_ctx);

    return 0;
}

问题已经解决,请看下面的代码

#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:/2.pcm";
const char *dst_media = "D:/2.mp3";

// PCM的原始参数
#define PCM_IN_FORMAT AV_SAMPLE_FMT_S16
#define PCM_IN_CHANNELS 1
#define PCM_IN_SAMPLE_RATE 44100

int main(int argc, char **argv) {
    // 申请一个输出的上下文
    NEW_PTR(AVFormatContext, fmt_ctx, nullptr, avio_closep(&fmt_ctx->pb); avformat_close_input(&fmt_ctx));
    avformat_alloc_output_context2(&fmt_ctx, nullptr, nullptr, dst_media);
    if (fmt_ctx == nullptr) {
        FATAL("alloc output format context failed.");
    }

    // 查询编码器
    AVCodec *audio_enc;
    if ((audio_enc = avcodec_find_encoder(fmt_ctx->oformat->audio_codec)) == nullptr) {
        FATAL("find audio encoder failed.");
    }

    // 为编码器申请上下文
    NEW_PTR(AVCodecContext, audio_enc_ctx, nullptr, avcodec_free_context(&audio_enc_ctx));
    if ((audio_enc_ctx = avcodec_alloc_context3(audio_enc)) == nullptr) {
        FATAL("allocate audio enc context failed.");
    }

    // 为编码器配置编码参数
    audio_enc_ctx->sample_fmt = audio_enc->sample_fmts ? audio_enc->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
    audio_enc_ctx->bit_rate = 64000;
    audio_enc_ctx->sample_rate = 44100;
    audio_enc_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
    audio_enc_ctx->channels = av_get_channel_layout_nb_channels(audio_enc_ctx->channel_layout);

    if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
        audio_enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

    // 打开编码器
    if (avcodec_open2(audio_enc_ctx, audio_enc, nullptr) < 0) {
        FATAL("codec open failed.");
    }

    AVStream *audio_stream = avformat_new_stream(fmt_ctx, audio_enc);
    audio_stream->id = fmt_ctx->nb_streams - 1;
    audio_stream->time_base = AVRational{1, audio_enc_ctx->sample_rate};

    if (avcodec_parameters_from_context(audio_stream->codecpar, audio_enc_ctx) < 0) {
        FATAL("copy params failed.");
    }

    if (avio_open(&fmt_ctx->pb, dst_media, AVIO_FLAG_WRITE) < 0) {
        FATAL("open dst file failed.");
    }

    // 写入头信息
    if (avformat_write_header(fmt_ctx, nullptr) < 0) {
        FATAL("write header failed.");
    }

    // 申请一个音频frame
    NEW_PTR(AVFrame, audio_frame, nullptr, av_frame_free(&audio_frame));
    if ((audio_frame = av_frame_alloc()) == nullptr) {
        FATAL("allocate frame failed.");
    }

    audio_frame->format = audio_enc_ctx->sample_fmt;
    audio_frame->channel_layout = audio_enc_ctx->channel_layout;
    audio_frame->nb_samples = audio_enc_ctx->frame_size;
    audio_frame->sample_rate = audio_enc_ctx->sample_rate;

    if (av_frame_get_buffer(audio_frame, 0) < 0) {
        FATAL("audio frame get buffer failed.");
    }

    // 创建一个frame,用来存储从pcm读取的数据
    NEW_PTR(AVFrame, buf_frame, nullptr, av_frame_free(&buf_frame));
    if ((buf_frame = av_frame_alloc()) == nullptr) {
        FATAL("allocate buf frame failed.");
    }
    buf_frame->format = PCM_IN_FORMAT;
    buf_frame->nb_samples = audio_frame->nb_samples;
    buf_frame->channel_layout = (uint64_t) av_get_default_channel_layout(PCM_IN_CHANNELS);
    buf_frame->sample_rate = PCM_IN_SAMPLE_RATE;

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

    // 从pcm文件中读取适应音频帧的尺寸数据
    auto readSize = av_samples_get_buffer_size(nullptr, buf_frame->channels, buf_frame->nb_samples,
                                               (AVSampleFormat) buf_frame->format, 1);
    NEW_PTR(uint8_t , buf, (uint8_t*)av_malloc((size_t)readSize), av_freep(&buf));

    // 创建swr的上下文
    NEW_PTR(SwrContext, swr_ctx, swr_alloc(), swr_free(&swr_ctx));
    swr_alloc_set_opts(swr_ctx,
                       audio_frame->channel_layout,
                       (AVSampleFormat)audio_frame->format,
                       audio_frame->sample_rate,
                       av_get_default_channel_layout(PCM_IN_CHANNELS),
                       PCM_IN_FORMAT,
                       PCM_IN_SAMPLE_RATE,
                       0, nullptr);
    swr_init(swr_ctx);

    // 申请一个packet,并初始化
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = nullptr;
    pkt.size = 0;

    NEW_PTR(FILE, input, nullptr, fclose(input));
    if ((input = fopen(src_media, "rb")) == nullptr) {
        FATAL("no readable file.");
    }

    // 循环读取frame数据
    int audio_pts = 0;
    while (true) {
        // 用来编码的帧
        AVFrame * encode_frame = nullptr;

        if (fread(buf, 1, (size_t) readSize, input) < 0) {
            FATAL("read input file failed.");
        } else if (!feof(input)) {
            // 文件没有到结尾,则获取编码帧
            av_samples_fill_arrays(buf_frame->data, buf_frame->linesize, (const uint8_t*)buf, buf_frame->channels, buf_frame->nb_samples, (AVSampleFormat)buf_frame->format, 1);

            swr_convert(swr_ctx, audio_frame->data, audio_frame->nb_samples, (const uint8_t**)buf_frame->data, buf_frame->nb_samples);

            audio_frame->pts = audio_pts;
            audio_pts += av_rescale_q(audio_frame->nb_samples, AVRational{1, audio_enc_ctx->frame_size},
                                 audio_enc_ctx->time_base);
            encode_frame = audio_frame;
        } else {
            // 文件结束了,则发送一个空指针的frame,用来清空缓冲区
            encode_frame = nullptr;
        }

        // 发送一个frame
        if (avcodec_send_frame(audio_enc_ctx, encode_frame) < 0) {
            FATAL("send frame exception.");
        }
        // 接受编码完的内容
        while (true) {
            auto packet_ret = avcodec_receive_packet(audio_enc_ctx, &pkt);
            // 判断是否完全接受了packet
            if (packet_ret == AVERROR(EAGAIN) || packet_ret == AVERROR_EOF) {
                break;
            }
            // 检查是否接受异常
            if (packet_ret < 0) {
                FATAL("receive packet exception.");
            }

            av_packet_rescale_ts(&pkt, audio_enc_ctx->time_base, audio_stream->time_base);
            pkt.stream_index = audio_stream->index;

            av_interleaved_write_frame(fmt_ctx, &pkt);

            av_packet_unref(&pkt);
        }

        // 编码帧为空,则表示已经处理完所有的编码,退出该循环
        if (encode_frame == nullptr) break;
    }

    av_write_trailer(fmt_ctx);

    return 0;
}

  • 写回答

1条回答

  • threenewbee 2019-10-10 16:17
    关注

    编码时长变化以后肯定会出现声音的扭曲,问题是,原来的pcm就不对,还是你的mp3编码不对。
    你可以用现成的软件提取、编码下,或者用ffmpeg编码成wav等无压缩格式,对比下,看看问题在哪里。

    评论

报告相同问题?

悬赏问题

  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?