使用FFmpeg API

如何解决使用FFmpeg API

我的任务是将RTP数据包的有效载荷数据写入音频文件。为此,在下面的示例中,我实现了一个音频编码器,该编码器需要少量的音频数据(例如pcm_alawpcm_mulawpcm_s16lepcm_s16be和等等)并将其保存到音频文件中。但是,尝试记录opusflacgsm等格式时会出现问题。 av_find_input_format 函数无法找到诸如opus之类的格式。而且我认为,是否可以将接收到的RTP数据包的音频数据发送到多路复用器?

如上所述,我附加了代码(仅适用于pcm_alawpcm_mulawpcm_s16lepcm_s16bepcm_s32lepcm_s32be ):

// For QT pro file
// INCLUDEPATH += /usr/include/ffmpeg
// QMAKE_CXXFLAGS += -D__STDC_CONSTANT_MACROS
// LIBS += -L/usr/local/lib -lz
// LIBS += -lm -lpthread -lavcodec -lavdevice -lavfilter -lavformat -lavresample -lavutil -lpostproc -lswresample -lswscale

#include <QDebug>
#include <QFile>

extern "C" {

#include <libavutil/log.h>
#include <libavutil/opt.h>
#include <libavutil/frame.h>
#include <libavformat/avio.h>
#include <libavutil/avassert.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avstring.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libavutil/audio_fifo.h>
#include <libswresample/swresample.h>
}

class AudioEncoderSettings {
public:
    static const QString DEF_OUTPUT_FILE;
    static const quint16 DEF_CHANNELS;
    static const quint32 DEF_SAMPLE_RATE;
    static const quint32 DEF_BIT_RATE;
    static const QString DEF_AUDIO_CODEC;

public:
    AudioEncoderSettings(void) = default;
    AudioEncoderSettings& operator=(const AudioEncoderSettings& other) = default;
    AudioEncoderSettings& operator=(AudioEncoderSettings&& other) = default;
    AudioEncoderSettings(const AudioEncoderSettings& other) = default;
    AudioEncoderSettings(AudioEncoderSettings&& other) = default;
    ~AudioEncoderSettings(void) = default;

    bool operator==(const AudioEncoderSettings& other) const;
    bool operator!=(const AudioEncoderSettings& other) const;

    quint32 sampleRate(void) const noexcept;
    quint16 channelCount(void) const noexcept;
    QString audioCodec(void) const noexcept;
    quint32 constBitRate(void) const noexcept;
    QString outputFile(void) const noexcept;

    void setSampleRate(const quint32& val) noexcept;
    void setChannelCount(const quint16& val) noexcept;
    void setAudioCodec(const QString& val) noexcept;
    void setConstBitRate(const quint32& val) noexcept;
    void setOutputFile(const QString& val) noexcept;

private:
    quint32 m_sampleRate{ DEF_SAMPLE_RATE };
    quint16 m_channelCount{ DEF_CHANNELS };
    QString m_audioCodec{ DEF_AUDIO_CODEC };
    quint32 m_constBitRate{ DEF_BIT_RATE };
    QString m_outputFile{ DEF_AUDIO_CODEC };
};

using Settings = AudioEncoderSettings;

const quint32 AudioEncoderSettings::DEF_SAMPLE_RATE = 0x1f40;
const quint16 AudioEncoderSettings::DEF_CHANNELS = 0x0001;
const QString AudioEncoderSettings::DEF_OUTPUT_FILE = QString();
const quint32 AudioEncoderSettings::DEF_BIT_RATE = 0xfa00;
const QString AudioEncoderSettings::DEF_AUDIO_CODEC = "alaw";

quint32 AudioEncoderSettings::sampleRate(void) const noexcept
{
    return m_sampleRate;
}

quint16 AudioEncoderSettings::channelCount(void) const noexcept
{
    return m_channelCount;
}

QString AudioEncoderSettings::audioCodec(void) const noexcept
{
    return m_audioCodec;
}

quint32 AudioEncoderSettings::constBitRate(void) const noexcept
{
    return m_constBitRate;
}

QString AudioEncoderSettings::outputFile(void) const noexcept
{
    return m_outputFile;
}

void AudioEncoderSettings::setSampleRate(const quint32& val) noexcept
{
    m_sampleRate = val;
}

void AudioEncoderSettings::setChannelCount(const quint16& val) noexcept
{
    m_channelCount = val;
}

void AudioEncoderSettings::setAudioCodec(const QString& val) noexcept
{
    m_audioCodec = val;
}

void AudioEncoderSettings::setConstBitRate(const quint32& val) noexcept
{
    m_constBitRate = val;
}

void AudioEncoderSettings::setOutputFile(const QString& val) noexcept
{
    m_outputFile = val;
}

bool AudioEncoderSettings::operator==(const AudioEncoderSettings& other) const
{
    return (m_sampleRate == other.m_sampleRate && m_channelCount == other.m_channelCount && m_audioCodec == other.m_audioCodec && m_constBitRate == other.m_constBitRate && m_outputFile == other.m_outputFile);
}

bool AudioEncoderSettings::operator!=(const AudioEncoderSettings& other) const
{
    return (m_sampleRate != other.m_sampleRate && m_channelCount != other.m_channelCount && m_audioCodec != other.m_audioCodec && m_constBitRate != other.m_constBitRate && m_outputFile != other.m_outputFile);
}

using AudioStr = AVStream;
using AudioCtx = AVIOContext;
using AudioDic = AVDictionary;
using AudioCdc = AVCodecContext;
using AudioFrm = AVFormatContext;

class AudioEncoder {
public:
    AudioEncoder(const Settings& settings);
    AudioEncoder& operator=(const AudioEncoder& rhs) = delete;
    AudioEncoder& operator=(AudioEncoder&& rhs) = delete;
    AudioEncoder(const AudioEncoder& rhs) = delete;
    AudioEncoder(AudioEncoder&& rhs) = delete;
    ~AudioEncoder(void) = default;

    bool init(void) noexcept;
    bool record(const QByteArray& rawData) noexcept;
    bool term(void) noexcept;

private:
    QString getMessageByErrorCode(const qint32& code) noexcept;
    bool proc(void) noexcept;

private:
    class Deleter {
    public:
        static void cleanup(AudioFrm* p);
        static void cleanup(AudioCdc* p);
        static void cleanup(AudioCtx* p);
        static void cleanup(AudioStr* p);
        static void cleanup(Settings* p);
        static void cleanup(AudioDic* p);
    };

    QScopedPointer<Settings,Deleter> p_sets{ nullptr };
    QScopedPointer<AudioStr,Deleter> p_iStr{ nullptr };
    QScopedPointer<AudioStr,Deleter> p_oStr{ nullptr };
    QScopedPointer<AudioCtx,Deleter> p_inIOCtx{ nullptr };
    QScopedPointer<AudioFrm,Deleter> p_iFrmCtx{ nullptr };
    QScopedPointer<AudioFrm,Deleter> p_oFrmCtx{ nullptr };

public:
    qsizetype m_curSize{};
    const uint8_t* p_curData{};
};

QString AudioEncoder::getMessageByErrorCode(const qint32& code) noexcept
{
    if (code != 0) {
        char errorBuffer[255]{ '0' };
        av_strerror(code,errorBuffer,sizeof(errorBuffer));
        return QString(errorBuffer);
    }
    return QString();
}

qint32 readPacket(void* opaque,quint8* buf,qint32 sz)
{
    AudioEncoder* self = static_cast<AudioEncoder*>(opaque);
    if (self->p_curData && self->m_curSize) {
        sz = std::min(sz,(int)self->m_curSize);
        memcpy(buf,self->p_curData,sz);
        self->m_curSize -= sz;
        self->p_curData += sz;
        return sz;
    }
    else {
        return AVERROR(EAGAIN);
    }
}

AudioEncoder::AudioEncoder(const Settings& settings)
    : p_sets(nullptr),p_iStr(nullptr),p_oStr(nullptr),p_inIOCtx(nullptr),p_iFrmCtx(nullptr),p_oFrmCtx(nullptr)
{
    p_sets.reset(new Settings(settings));
}

void AudioEncoder::Deleter::cleanup(AudioFrm* p)
{
    if (p != nullptr)
        avformat_close_input(&p);
}

void AudioEncoder::Deleter::cleanup(AudioCdc* p)
{
    if (p != nullptr)
        avcodec_free_context(&p);
}

void AudioEncoder::Deleter::cleanup(AudioCtx* p)
{
    if (p != nullptr)
        av_freep(&p->buffer);
    avio_context_free(&p);
}

void AudioEncoder::Deleter::cleanup(AudioStr* p)
{
    if (p != nullptr)
        p = nullptr;
}

void AudioEncoder::Deleter::cleanup(Settings* p)
{
    if (p != nullptr)
        delete p;
}

void AudioEncoder::Deleter::cleanup(AudioDic* p)
{
    if (p != nullptr)
        av_dict_free(&p);
}

bool AudioEncoder::init(void) noexcept
{
    if (p_oFrmCtx) {
        return true;
    }
    av_register_all();
    avcodec_register_all();

    AVInputFormat* file_iformat = av_find_input_format(p_sets->audioCodec().toStdString().c_str());
    if (file_iformat == nullptr) {
        qDebug() << QString("Unknown input format: '%1'").arg(p_sets->audioCodec());
        return false;
    }

    AudioDic* format_opts = nullptr;
    const qint32 sampleRateErrorCode = av_dict_set(&format_opts,"sample_rate",QString::number(p_sets->sampleRate()).toStdString().c_str(),0);
    const qint32 bitRateErrorCode = av_dict_set(&format_opts,"bit_rate",QString::number(p_sets->constBitRate()).toStdString().c_str(),0);
    qint32 channelErrorCode = 0;

    // because we set audio_channels based on both the "ac" and
    // "channel_layout" options,we need to check that the specified
    // demuxer actually has the "channels" option before setting it
    if (file_iformat && file_iformat->priv_class && av_opt_find(&file_iformat->priv_class,"channels",NULL,AV_OPT_SEARCH_FAKE_OBJ)) {
        channelErrorCode = av_dict_set(&format_opts,QString::number(p_sets->channelCount()).toStdString().c_str(),0);
    }

    if ((bitRateErrorCode < 0) || (sampleRateErrorCode < 0) || (channelErrorCode < 0)) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        return false;
    }

    AVFormatContext* ic;
    /* get default parameters from command line */
    ic = avformat_alloc_context();
    if (!ic) {
        qDebug() << "Error: " << __LINE__;
        return false;
    }

    const qint32 iBufSize = 4096;
    quint8* iCtxBuffer = static_cast<quint8*>(av_malloc(iBufSize));
    if (!iCtxBuffer) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        return false;
    }

    p_inIOCtx.reset(avio_alloc_context(
        iCtxBuffer,iBufSize,this,&readPacket,nullptr,nullptr));
    if (!p_inIOCtx) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);
        return false;
    }

    ic->pb = p_inIOCtx.get();
    int errorCode = 0;
    if ((errorCode = avformat_open_input(&ic,p_sets->outputFile().toStdString().c_str(),file_iformat,&format_opts))
        < 0) {
        ic = nullptr;
        qDebug() << QString("Could not open output file: %1 (error: %2)").arg(p_sets->outputFile()).arg(getMessageByErrorCode(errorCode));
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);
        return false;
    }
    p_iFrmCtx.reset(ic);
    if (p_iFrmCtx->nb_streams != 1) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);
        return false;
    }

    p_iStr.reset(p_iFrmCtx->streams[0]);
    AVCodec* iCdc = avcodec_find_decoder(p_iStr->codecpar->codec_id);
    if (!iCdc) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);
        return false;
    }
    qDebug() << "Decoder found: " << iCdc->name;

    AudioCdc* iCdcCtx = avcodec_alloc_context3(iCdc);
    if (!iCdcCtx) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);
        return false;
    }

    avcodec_parameters_to_context(iCdcCtx,p_iStr->codecpar);
    if (avcodec_open2(iCdcCtx,iCdc,&format_opts) < 0) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);
        return false;
    }

    int ret = avcodec_parameters_from_context(p_iStr->codecpar,iCdcCtx);
    if (ret < 0) {
        qDebug() << "Error initializing the decoder context";
        return false;
    }

    // Open output file ........
    AVDictionary* opts = nullptr;
    av_dict_copy(&opts,format_opts,0);

    AudioFrm* f = nullptr;
    if (avformat_alloc_output_context2(
            &f,p_sets->outputFile().toStdString().c_str())
        < 0) {

        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);
        avcodec_free_context(&iCdcCtx);
        return false;
    }

    p_oFrmCtx.reset(f);
    if (!(p_oFrmCtx->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&p_oFrmCtx->pb,AVIO_FLAG_WRITE)
            < 0) {
            if (format_opts != nullptr)
                av_dict_free(&format_opts);
            av_free(iCtxBuffer);
            avcodec_free_context(&iCdcCtx);
            return false;
        }
    }

    p_oStr.reset(avformat_new_stream(p_oFrmCtx.get(),NULL));
    if (!p_oStr) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);
        avcodec_free_context(&iCdcCtx);
        return false;
    }

    if (avcodec_parameters_copy(p_oStr->codecpar,p_iStr->codecpar) < 0) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);
        avcodec_free_context(&iCdcCtx);
        return false;
    }

    p_oStr->codecpar->codec_tag = 0;
    av_dict_free(&format_opts);
    if (avformat_write_header(p_oFrmCtx.get(),0) < 0) {
        if (format_opts != nullptr)
            av_dict_free(&format_opts);
        av_free(iCtxBuffer);

        avcodec_free_context(&iCdcCtx);
        return false;
    }
    avcodec_free_context(&iCdcCtx);
    return true;
}

bool AudioEncoder::proc(void) noexcept
{
    AVPacket pkt{};
    while (true) {
        const qint32 rc = av_read_frame(p_iFrmCtx.get(),&pkt);
        if (rc < 0) {
            return false;
        }
        if (rc == AVERROR(EAGAIN) || rc == AVERROR_EOF) {
            break;
        }
        if (pkt.stream_index == p_iStr->index) {

            pkt.pts = av_rescale_q_rnd(pkt.pts,p_iStr->time_base,p_oStr->time_base,static_cast<enum AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            pkt.dts = av_rescale_q_rnd(pkt.dts,static_cast<enum AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));

            pkt.duration = av_rescale_q(pkt.duration,p_oStr->time_base);
            pkt.pos = -1;
            if (av_interleaved_write_frame(
                    p_oFrmCtx.get(),&pkt)
                < 0) {
                av_packet_unref(&pkt);
                return false;
            }
            av_packet_unref(&pkt);
        }
    }
    return m_curSize == 0;
}

bool AudioEncoder::record(const QByteArray& rawData) noexcept
{
    if (p_oFrmCtx) {
        if (!rawData.isEmpty()) {
            if (p_inIOCtx->error == AVERROR(EAGAIN)) {
                p_inIOCtx->eof_reached = 0;
            }
            p_curData = reinterpret_cast<const uint8_t*>(rawData.data());
            m_curSize = rawData.size();
            return proc();
        }
    }
    return false;
}

bool AudioEncoder::term(void) noexcept
{
    if (p_oFrmCtx) {
        proc();
        qint32 error = 0;
        if ((error = av_write_trailer(p_oFrmCtx.get())) < 0) {
            qDebug() << QString("Could not write output file "
                                "trailer (error '%1')")
                            .arg(getMessageByErrorCode(error));
            return false;
        }
        p_iStr.reset();
        p_oStr.reset();
        p_inIOCtx.reset();
        p_iFrmCtx.reset();
        p_oFrmCtx.reset();
        return true;
    }
    return false;
}

int main()
{
    AudioEncoderSettings settings;
    settings.setAudioCodec("alaw");
    settings.setOutputFile("/home/test/result.mka");
    settings.setSampleRate(8000);
    settings.setChannelCount(1);
    settings.setConstBitRate(64000);

    AudioEncoder encoder(settings);
    if (encoder.init()) {
        QFile file("/home/test/rawAlawRtpPayloadData.bin");
        file.open(QIODevice::ReadOnly);
        QByteArray arr(file.readAll());
        if (encoder.record(arr)) {
            return encoder.term();
        }
    }
    return encoder.term();
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res