Recherche avancée

Médias (0)

Mot : - Tags -/navigation

Aucun média correspondant à vos critères n’est disponible sur le site.

Autres articles (108)

  • Emballe médias : à quoi cela sert ?

    4 février 2011, par

    Ce plugin vise à gérer des sites de mise en ligne de documents de tous types.
    Il crée des "médias", à savoir : un "média" est un article au sens SPIP créé automatiquement lors du téléversement d’un document qu’il soit audio, vidéo, image ou textuel ; un seul document ne peut être lié à un article dit "média" ;

  • Ajouter des informations spécifiques aux utilisateurs et autres modifications de comportement liées aux auteurs

    12 avril 2011, par

    La manière la plus simple d’ajouter des informations aux auteurs est d’installer le plugin Inscription3. Il permet également de modifier certains comportements liés aux utilisateurs (référez-vous à sa documentation pour plus d’informations).
    Il est également possible d’ajouter des champs aux auteurs en installant les plugins champs extras 2 et Interface pour champs extras.

  • Encoding and processing into web-friendly formats

    13 avril 2011, par

    MediaSPIP automatically converts uploaded files to internet-compatible formats.
    Video files are encoded in MP4, Ogv and WebM (supported by HTML5) and MP4 (supported by Flash).
    Audio files are encoded in MP3 and Ogg (supported by HTML5) and MP3 (supported by Flash).
    Where possible, text is analyzed in order to retrieve the data needed for search engine detection, and then exported as a series of image files.
    All uploaded files are stored online in their original format, so you can (...)

Sur d’autres sites (12012)

  • Write RTP payload data to an audio file using FFmpeg API

    6 novembre 2020, par bbdd

    My task is to write the payload data of an RTP packet to an audio file. To do this, in the example below, I implemented an audio encoder that takes a small amount of audio data (such as pcm_alaw, pcm_mulaw, pcm_s16le, pcm_s16be, and so on) and saves it to an audio file. But there is a problem when trying to record formats such as opus, flac, gsm, and so on. The av_find_input_format function cannot find formats such as opus. And I thought, is it possible to just send the received audio data of the RTP packet to muxer ?

    


    As mentioned above, I attach the code (only works with pcm_alaw, pcm_mulaw, pcm_s16le, pcm_s16be, pcm_s32le, pcm_s32be) :

    


    // For QT pro file&#xA;// INCLUDEPATH &#x2B;= /usr/include/ffmpeg&#xA;// QMAKE_CXXFLAGS &#x2B;= -D__STDC_CONSTANT_MACROS&#xA;// LIBS &#x2B;= -L/usr/local/lib -lz&#xA;// LIBS &#x2B;= -lm -lpthread -lavcodec -lavdevice -lavfilter -lavformat -lavresample -lavutil -lpostproc -lswresample -lswscale&#xA;&#xA;#include <qdebug>&#xA;#include <qfile>&#xA;&#xA;extern "C" {&#xA;&#xA;#include <libavutil></libavutil>log.h>&#xA;#include <libavutil></libavutil>opt.h>&#xA;#include <libavutil></libavutil>frame.h>&#xA;#include <libavformat></libavformat>avio.h>&#xA;#include <libavutil></libavutil>avassert.h>&#xA;#include <libavcodec></libavcodec>avcodec.h>&#xA;#include <libavutil></libavutil>avstring.h>&#xA;#include <libavfilter></libavfilter>avfilter.h>&#xA;#include <libavformat></libavformat>avformat.h>&#xA;#include <libavutil></libavutil>audio_fifo.h>&#xA;#include <libswresample></libswresample>swresample.h>&#xA;}&#xA;&#xA;class AudioEncoderSettings {&#xA;public:&#xA;    static const QString DEF_OUTPUT_FILE;&#xA;    static const quint16 DEF_CHANNELS;&#xA;    static const quint32 DEF_SAMPLE_RATE;&#xA;    static const quint32 DEF_BIT_RATE;&#xA;    static const QString DEF_AUDIO_CODEC;&#xA;&#xA;public:&#xA;    AudioEncoderSettings(void) = default;&#xA;    AudioEncoderSettings&amp; operator=(const AudioEncoderSettings&amp; other) = default;&#xA;    AudioEncoderSettings&amp; operator=(AudioEncoderSettings&amp;&amp; other) = default;&#xA;    AudioEncoderSettings(const AudioEncoderSettings&amp; other) = default;&#xA;    AudioEncoderSettings(AudioEncoderSettings&amp;&amp; other) = default;&#xA;    ~AudioEncoderSettings(void) = default;&#xA;&#xA;    bool operator==(const AudioEncoderSettings&amp; other) const;&#xA;    bool operator!=(const AudioEncoderSettings&amp; other) const;&#xA;&#xA;    quint32 sampleRate(void) const noexcept;&#xA;    quint16 channelCount(void) const noexcept;&#xA;    QString audioCodec(void) const noexcept;&#xA;    quint32 constBitRate(void) const noexcept;&#xA;    QString outputFile(void) const noexcept;&#xA;&#xA;    void setSampleRate(const quint32&amp; val) noexcept;&#xA;    void setChannelCount(const quint16&amp; val) noexcept;&#xA;    void setAudioCodec(const QString&amp; val) noexcept;&#xA;    void setConstBitRate(const quint32&amp; val) noexcept;&#xA;    void setOutputFile(const QString&amp; val) noexcept;&#xA;&#xA;private:&#xA;    quint32 m_sampleRate{ DEF_SAMPLE_RATE };&#xA;    quint16 m_channelCount{ DEF_CHANNELS };&#xA;    QString m_audioCodec{ DEF_AUDIO_CODEC };&#xA;    quint32 m_constBitRate{ DEF_BIT_RATE };&#xA;    QString m_outputFile{ DEF_AUDIO_CODEC };&#xA;};&#xA;&#xA;using Settings = AudioEncoderSettings;&#xA;&#xA;const quint32 AudioEncoderSettings::DEF_SAMPLE_RATE = 0x1f40;&#xA;const quint16 AudioEncoderSettings::DEF_CHANNELS = 0x0001;&#xA;const QString AudioEncoderSettings::DEF_OUTPUT_FILE = QString();&#xA;const quint32 AudioEncoderSettings::DEF_BIT_RATE = 0xfa00;&#xA;const QString AudioEncoderSettings::DEF_AUDIO_CODEC = "alaw";&#xA;&#xA;quint32 AudioEncoderSettings::sampleRate(void) const noexcept&#xA;{&#xA;    return m_sampleRate;&#xA;}&#xA;&#xA;quint16 AudioEncoderSettings::channelCount(void) const noexcept&#xA;{&#xA;    return m_channelCount;&#xA;}&#xA;&#xA;QString AudioEncoderSettings::audioCodec(void) const noexcept&#xA;{&#xA;    return m_audioCodec;&#xA;}&#xA;&#xA;quint32 AudioEncoderSettings::constBitRate(void) const noexcept&#xA;{&#xA;    return m_constBitRate;&#xA;}&#xA;&#xA;QString AudioEncoderSettings::outputFile(void) const noexcept&#xA;{&#xA;    return m_outputFile;&#xA;}&#xA;&#xA;void AudioEncoderSettings::setSampleRate(const quint32&amp; val) noexcept&#xA;{&#xA;    m_sampleRate = val;&#xA;}&#xA;&#xA;void AudioEncoderSettings::setChannelCount(const quint16&amp; val) noexcept&#xA;{&#xA;    m_channelCount = val;&#xA;}&#xA;&#xA;void AudioEncoderSettings::setAudioCodec(const QString&amp; val) noexcept&#xA;{&#xA;    m_audioCodec = val;&#xA;}&#xA;&#xA;void AudioEncoderSettings::setConstBitRate(const quint32&amp; val) noexcept&#xA;{&#xA;    m_constBitRate = val;&#xA;}&#xA;&#xA;void AudioEncoderSettings::setOutputFile(const QString&amp; val) noexcept&#xA;{&#xA;    m_outputFile = val;&#xA;}&#xA;&#xA;bool AudioEncoderSettings::operator==(const AudioEncoderSettings&amp; other) const&#xA;{&#xA;    return (m_sampleRate == other.m_sampleRate &amp;&amp; m_channelCount == other.m_channelCount &amp;&amp; m_audioCodec == other.m_audioCodec &amp;&amp; m_constBitRate == other.m_constBitRate &amp;&amp; m_outputFile == other.m_outputFile);&#xA;}&#xA;&#xA;bool AudioEncoderSettings::operator!=(const AudioEncoderSettings&amp; other) const&#xA;{&#xA;    return (m_sampleRate != other.m_sampleRate &amp;&amp; m_channelCount != other.m_channelCount &amp;&amp; m_audioCodec != other.m_audioCodec &amp;&amp; m_constBitRate != other.m_constBitRate &amp;&amp; m_outputFile != other.m_outputFile);&#xA;}&#xA;&#xA;using AudioStr = AVStream;&#xA;using AudioCtx = AVIOContext;&#xA;using AudioDic = AVDictionary;&#xA;using AudioCdc = AVCodecContext;&#xA;using AudioFrm = AVFormatContext;&#xA;&#xA;class AudioEncoder {&#xA;public:&#xA;    AudioEncoder(const Settings&amp; settings);&#xA;    AudioEncoder&amp; operator=(const AudioEncoder&amp; rhs) = delete;&#xA;    AudioEncoder&amp; operator=(AudioEncoder&amp;&amp; rhs) = delete;&#xA;    AudioEncoder(const AudioEncoder&amp; rhs) = delete;&#xA;    AudioEncoder(AudioEncoder&amp;&amp; rhs) = delete;&#xA;    ~AudioEncoder(void) = default;&#xA;&#xA;    bool init(void) noexcept;&#xA;    bool record(const QByteArray&amp; rawData) noexcept;&#xA;    bool term(void) noexcept;&#xA;&#xA;private:&#xA;    QString getMessageByErrorCode(const qint32&amp; code) noexcept;&#xA;    bool proc(void) noexcept;&#xA;&#xA;private:&#xA;    class Deleter {&#xA;    public:&#xA;        static void cleanup(AudioFrm* p);&#xA;        static void cleanup(AudioCdc* p);&#xA;        static void cleanup(AudioCtx* p);&#xA;        static void cleanup(AudioStr* p);&#xA;        static void cleanup(Settings* p);&#xA;        static void cleanup(AudioDic* p);&#xA;    };&#xA;&#xA;    QScopedPointer p_sets{ nullptr };&#xA;    QScopedPointer p_iStr{ nullptr };&#xA;    QScopedPointer p_oStr{ nullptr };&#xA;    QScopedPointer p_inIOCtx{ nullptr };&#xA;    QScopedPointer p_iFrmCtx{ nullptr };&#xA;    QScopedPointer p_oFrmCtx{ nullptr };&#xA;&#xA;public:&#xA;    qsizetype m_curSize{};&#xA;    const uint8_t* p_curData{};&#xA;};&#xA;&#xA;QString AudioEncoder::getMessageByErrorCode(const qint32&amp; code) noexcept&#xA;{&#xA;    if (code != 0) {&#xA;        char errorBuffer[255]{ &#x27;0&#x27; };&#xA;        av_strerror(code, errorBuffer, sizeof(errorBuffer));&#xA;        return QString(errorBuffer);&#xA;    }&#xA;    return QString();&#xA;}&#xA;&#xA;qint32 readPacket(void* opaque, quint8* buf, qint32 sz)&#xA;{&#xA;    AudioEncoder* self = static_cast(opaque);&#xA;    if (self->p_curData &amp;&amp; self->m_curSize) {&#xA;        sz = std::min(sz, (int)self->m_curSize);&#xA;        memcpy(buf, self->p_curData, sz);&#xA;        self->m_curSize -= sz;&#xA;        self->p_curData &#x2B;= sz;&#xA;        return sz;&#xA;    }&#xA;    else {&#xA;        return AVERROR(EAGAIN);&#xA;    }&#xA;}&#xA;&#xA;AudioEncoder::AudioEncoder(const Settings&amp; settings)&#xA;    : p_sets(nullptr)&#xA;    , p_iStr(nullptr)&#xA;    , p_oStr(nullptr)&#xA;    , p_inIOCtx(nullptr)&#xA;    , p_iFrmCtx(nullptr)&#xA;    , p_oFrmCtx(nullptr)&#xA;{&#xA;    p_sets.reset(new Settings(settings));&#xA;}&#xA;&#xA;void AudioEncoder::Deleter::cleanup(AudioFrm* p)&#xA;{&#xA;    if (p != nullptr)&#xA;        avformat_close_input(&amp;p);&#xA;}&#xA;&#xA;void AudioEncoder::Deleter::cleanup(AudioCdc* p)&#xA;{&#xA;    if (p != nullptr)&#xA;        avcodec_free_context(&amp;p);&#xA;}&#xA;&#xA;void AudioEncoder::Deleter::cleanup(AudioCtx* p)&#xA;{&#xA;    if (p != nullptr)&#xA;        av_freep(&amp;p->buffer);&#xA;    avio_context_free(&amp;p);&#xA;}&#xA;&#xA;void AudioEncoder::Deleter::cleanup(AudioStr* p)&#xA;{&#xA;    if (p != nullptr)&#xA;        p = nullptr;&#xA;}&#xA;&#xA;void AudioEncoder::Deleter::cleanup(Settings* p)&#xA;{&#xA;    if (p != nullptr)&#xA;        delete p;&#xA;}&#xA;&#xA;void AudioEncoder::Deleter::cleanup(AudioDic* p)&#xA;{&#xA;    if (p != nullptr)&#xA;        av_dict_free(&amp;p);&#xA;}&#xA;&#xA;bool AudioEncoder::init(void) noexcept&#xA;{&#xA;    if (p_oFrmCtx) {&#xA;        return true;&#xA;    }&#xA;    av_register_all();&#xA;    avcodec_register_all();&#xA;&#xA;    AVInputFormat* file_iformat = av_find_input_format(p_sets->audioCodec().toStdString().c_str());&#xA;    if (file_iformat == nullptr) {&#xA;        qDebug() &lt;&lt; QString("Unknown input format: &#x27;%1&#x27;").arg(p_sets->audioCodec());&#xA;        return false;&#xA;    }&#xA;&#xA;    AudioDic* format_opts = nullptr;&#xA;    const qint32 sampleRateErrorCode = av_dict_set(&amp;format_opts, "sample_rate",&#xA;        QString::number(p_sets->sampleRate()).toStdString().c_str(), 0);&#xA;    const qint32 bitRateErrorCode = av_dict_set(&amp;format_opts, "bit_rate",&#xA;        QString::number(p_sets->constBitRate()).toStdString().c_str(), 0);&#xA;    qint32 channelErrorCode = 0;&#xA;&#xA;    // because we set audio_channels based on both the "ac" and&#xA;    // "channel_layout" options, we need to check that the specified&#xA;    // demuxer actually has the "channels" option before setting it&#xA;    if (file_iformat &amp;&amp; file_iformat->priv_class &amp;&amp; av_opt_find(&amp;file_iformat->priv_class, "channels", NULL, 0, AV_OPT_SEARCH_FAKE_OBJ)) {&#xA;        channelErrorCode = av_dict_set(&amp;format_opts, "channels",&#xA;            QString::number(p_sets->channelCount()).toStdString().c_str(), 0);&#xA;    }&#xA;&#xA;    if ((bitRateErrorCode &lt; 0) || (sampleRateErrorCode &lt; 0) || (channelErrorCode &lt; 0)) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        return false;&#xA;    }&#xA;&#xA;    AVFormatContext* ic;&#xA;    /* get default parameters from command line */&#xA;    ic = avformat_alloc_context();&#xA;    if (!ic) {&#xA;        qDebug() &lt;&lt; "Error: " &lt;&lt; __LINE__;&#xA;        return false;&#xA;    }&#xA;&#xA;    const qint32 iBufSize = 4096;&#xA;    quint8* iCtxBuffer = static_cast(av_malloc(iBufSize));&#xA;    if (!iCtxBuffer) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        return false;&#xA;    }&#xA;&#xA;    p_inIOCtx.reset(avio_alloc_context(&#xA;        iCtxBuffer, iBufSize, 0, this, &amp;readPacket, nullptr, nullptr));&#xA;    if (!p_inIOCtx) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;        return false;&#xA;    }&#xA;&#xA;    ic->pb = p_inIOCtx.get();&#xA;    int errorCode = 0;&#xA;    if ((errorCode = avformat_open_input(&amp;ic,&#xA;             p_sets->outputFile().toStdString().c_str(), file_iformat, &amp;format_opts))&#xA;        &lt; 0) {&#xA;        ic = nullptr;&#xA;        qDebug() &lt;&lt; QString("Could not open output file: %1 (error: %2)").arg(p_sets->outputFile()).arg(getMessageByErrorCode(errorCode));&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;        return false;&#xA;    }&#xA;    p_iFrmCtx.reset(ic);&#xA;    if (p_iFrmCtx->nb_streams != 1) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;        return false;&#xA;    }&#xA;&#xA;    p_iStr.reset(p_iFrmCtx->streams[0]);&#xA;    AVCodec* iCdc = avcodec_find_decoder(p_iStr->codecpar->codec_id);&#xA;    if (!iCdc) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;        return false;&#xA;    }&#xA;    qDebug() &lt;&lt; "Decoder found: " &lt;&lt; iCdc->name;&#xA;&#xA;    AudioCdc* iCdcCtx = avcodec_alloc_context3(iCdc);&#xA;    if (!iCdcCtx) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;        return false;&#xA;    }&#xA;&#xA;    avcodec_parameters_to_context(iCdcCtx, p_iStr->codecpar);&#xA;    if (avcodec_open2(iCdcCtx, iCdc, &amp;format_opts) &lt; 0) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;        return false;&#xA;    }&#xA;&#xA;    int ret = avcodec_parameters_from_context(p_iStr->codecpar, iCdcCtx);&#xA;    if (ret &lt; 0) {&#xA;        qDebug() &lt;&lt; "Error initializing the decoder context";&#xA;        return false;&#xA;    }&#xA;&#xA;    // Open output file ........&#xA;    AVDictionary* opts = nullptr;&#xA;    av_dict_copy(&amp;opts, format_opts, 0);&#xA;&#xA;    AudioFrm* f = nullptr;&#xA;    if (avformat_alloc_output_context2(&#xA;            &amp;f,&#xA;            nullptr,&#xA;            nullptr,&#xA;            p_sets->outputFile().toStdString().c_str())&#xA;        &lt; 0) {&#xA;&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;        avcodec_free_context(&amp;iCdcCtx);&#xA;        return false;&#xA;    }&#xA;&#xA;    p_oFrmCtx.reset(f);&#xA;    if (!(p_oFrmCtx->oformat->flags &amp; AVFMT_NOFILE)) {&#xA;        if (avio_open(&amp;p_oFrmCtx->pb,&#xA;                p_sets->outputFile().toStdString().c_str(), AVIO_FLAG_WRITE)&#xA;            &lt; 0) {&#xA;            if (format_opts != nullptr)&#xA;                av_dict_free(&amp;format_opts);&#xA;            av_free(iCtxBuffer);&#xA;            avcodec_free_context(&amp;iCdcCtx);&#xA;            return false;&#xA;        }&#xA;    }&#xA;&#xA;    p_oStr.reset(avformat_new_stream(p_oFrmCtx.get(), NULL));&#xA;    if (!p_oStr) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;        avcodec_free_context(&amp;iCdcCtx);&#xA;        return false;&#xA;    }&#xA;&#xA;    if (avcodec_parameters_copy(p_oStr->codecpar, p_iStr->codecpar) &lt; 0) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;        avcodec_free_context(&amp;iCdcCtx);&#xA;        return false;&#xA;    }&#xA;&#xA;    p_oStr->codecpar->codec_tag = 0;&#xA;    av_dict_free(&amp;format_opts);&#xA;    if (avformat_write_header(p_oFrmCtx.get(), 0) &lt; 0) {&#xA;        if (format_opts != nullptr)&#xA;            av_dict_free(&amp;format_opts);&#xA;        av_free(iCtxBuffer);&#xA;&#xA;        avcodec_free_context(&amp;iCdcCtx);&#xA;        return false;&#xA;    }&#xA;    avcodec_free_context(&amp;iCdcCtx);&#xA;    return true;&#xA;}&#xA;&#xA;bool AudioEncoder::proc(void) noexcept&#xA;{&#xA;    AVPacket pkt{};&#xA;    while (true) {&#xA;        const qint32 rc = av_read_frame(p_iFrmCtx.get(), &amp;pkt);&#xA;        if (rc &lt; 0) {&#xA;            return false;&#xA;        }&#xA;        if (rc == AVERROR(EAGAIN) || rc == AVERROR_EOF) {&#xA;            break;&#xA;        }&#xA;        if (pkt.stream_index == p_iStr->index) {&#xA;&#xA;            pkt.pts = av_rescale_q_rnd(pkt.pts, p_iStr->time_base, p_oStr->time_base,&#xA;                static_cast<enum avrounding="avrounding">(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));&#xA;            pkt.dts = av_rescale_q_rnd(pkt.dts, p_iStr->time_base, p_oStr->time_base,&#xA;                static_cast<enum avrounding="avrounding">(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));&#xA;&#xA;            pkt.duration = av_rescale_q(pkt.duration, p_iStr->time_base, p_oStr->time_base);&#xA;            pkt.pos = -1;&#xA;            if (av_interleaved_write_frame(&#xA;                    p_oFrmCtx.get(), &amp;pkt)&#xA;                &lt; 0) {&#xA;                av_packet_unref(&amp;pkt);&#xA;                return false;&#xA;            }&#xA;            av_packet_unref(&amp;pkt);&#xA;        }&#xA;    }&#xA;    return m_curSize == 0;&#xA;}&#xA;&#xA;bool AudioEncoder::record(const QByteArray&amp; rawData) noexcept&#xA;{&#xA;    if (p_oFrmCtx) {&#xA;        if (!rawData.isEmpty()) {&#xA;            if (p_inIOCtx->error == AVERROR(EAGAIN)) {&#xA;                p_inIOCtx->eof_reached = 0;&#xA;            }&#xA;            p_curData = reinterpret_cast<const>(rawData.data());&#xA;            m_curSize = rawData.size();&#xA;            return proc();&#xA;        }&#xA;    }&#xA;    return false;&#xA;}&#xA;&#xA;bool AudioEncoder::term(void) noexcept&#xA;{&#xA;    if (p_oFrmCtx) {&#xA;        proc();&#xA;        qint32 error = 0;&#xA;        if ((error = av_write_trailer(p_oFrmCtx.get())) &lt; 0) {&#xA;            qDebug() &lt;&lt; QString("Could not write output file "&#xA;                                "trailer (error &#x27;%1&#x27;)")&#xA;                            .arg(getMessageByErrorCode(error));&#xA;            return false;&#xA;        }&#xA;        p_iStr.reset();&#xA;        p_oStr.reset();&#xA;        p_inIOCtx.reset();&#xA;        p_iFrmCtx.reset();&#xA;        p_oFrmCtx.reset();&#xA;        return true;&#xA;    }&#xA;    return false;&#xA;}&#xA;&#xA;int main()&#xA;{&#xA;    AudioEncoderSettings settings;&#xA;    settings.setAudioCodec("alaw");&#xA;    settings.setOutputFile("/home/test/result.mka");&#xA;    settings.setSampleRate(8000);&#xA;    settings.setChannelCount(1);&#xA;    settings.setConstBitRate(64000);&#xA;&#xA;    AudioEncoder encoder(settings);&#xA;    if (encoder.init()) {&#xA;        QFile file("/home/test/rawAlawRtpPayloadData.bin");&#xA;        file.open(QIODevice::ReadOnly);&#xA;        QByteArray arr(file.readAll());&#xA;        if (encoder.record(arr)) {&#xA;            return encoder.term();&#xA;        }&#xA;    }&#xA;    return encoder.term();&#xA;}&#xA;&#xA;</const></enum></enum></qfile></qdebug>

    &#xA;

  • ISO-9660 Compromise, Part 2 : Finding Root

    25 octobre 2021, par Multimedia Mike — General

    A long time ago, I dashed off a quick blog post with a curious finding after studying the ISO-9660 spec : The format stores multi-byte numbers in a format I termed “omni-endian”– the committee developing the format apparently couldn’t come to an agreement on this basic point regarding big- vs. little-endian encoding (I’m envisioning something along the lines of “tastes great ! … less filling !” in the committee meetings).

    I recently discovered another bit of compromise in the ISO-9660 spec : It seems that there are 2 different methods for processing the directory structure. That means it’s incumbent upon ISO-9660 creation software to fill in the data structures to support both methods, because some ISO-reading programs out there rely on one set of data structures while the rest prefer to read the other set.

    Background

    As a refresher, the “ISO” extension of an ISO file refers to the ISO-9660 specification. This is a type of read-only filesystem (i.e, the filesystem is created once and never updated after initial creation) for the purpose of storing on a read-only medium, often an optical disc (CD-ROM, DVD-ROM). The level of nostalgic interest I display for the ISO-9660 filesystem reminds me of my computer science curriculum professors from the mid-90s reminiscing about ye olden days of punchcard programming, but such is my lot. I’m probably also alone in my frustration of seeing rips of, e.g., GameCube or Xbox or 3DO games being tagged with the extension .ISO since those systems use different read-only filesystems.

    I recently fell in with an odd bunch called the eXoDOS project and was trying to help fill in a few gaps. One request was a 1994 game called Power Drive for DOS.


    Power Drive CD-ROM


    My usual CD-ROM ripping method (for the data track) is a simple ‘dd’ command from a Linux command line to copy the string of raw sectors. However, it turned out to be unusually difficult to open the resulting ISO. A few of the the options I know of worked but most didn’t. What’s the difference ?

    Methods that work :

    • Mounting the file with the Linux iso9660 kernel module, i.e.,
      mount -t iso9660 /dev/optical-drive /mnt

      or

      mount -t iso9660 -o loop /path/to/Power-Drive.iso /mnt
    • Directory Opus
    • Windows 10 can read the filesystem when reading the physical disc
    • Windows 10 can burn the ISO image to a new CD (“right click” -> “Burn disc image”) ; this method does not modify any of the existing sectors but did append 149 additional empty sectors

    Methods that don’t work :

    Understanding The Difference

    I think I might have a handle on why some tools are able to process this disc while most can’t. There appears to be 2 sets of data structures to describe the base of the filesystem : A root directory, and a path table. These both occur in the first substantive sector of the ISO-9660 filesystem, usually sector 16.

    A compact disc can be abstractly visualized as a long string of sectors, each one 2,352 bytes long. (See my Grand Unified Theory of Compact Disc post for deeper discussion.) A CD-ROM data track will contain 2048 bytes of data. Thus, sector 16 appears at 0x8000 of an ISO filesystem. I like the clarity of this description of the ISO-9660 spec. It shows that the path table is defined at byte 140 (little-endian ; big comes later) and location of the root directory is at byte 158. Thus, these locations generally occur at 0x808c and 0x809e.


    Primary Volume Descriptor
    Primary Volume Descriptor

    The path table is highlighted in green and the root directory record is highlighted in red. These absolute locations are specified in sectors. So the path table is located at sector 0x12 = offset 0x9000 in the image, while the root directory record is supposed to be at sector 0x62 = 0x31000. Checking into those sectors, it turns out that the path table is valid while the root directory record is invalid. Thus, any tool that relies on the path table will be successful in interpreting the disc, while tools that attempt to recursively traverse starting from root directory record are gonna have a bad time.

    Since I was able to view the filesystem with a few different tools, I know what the root directory contains. Searching for those filenames reveals that the root directory was supposed to point to the next sector, number 0x63. So this was a bizarre off-by-1 error on the part of the ISO creation tool. Maybe. I manually corrected 0x62 -> 0x63 and that fixed the interaction with fuseiso, but not with other tools. So there may have been some other errors. Note that a quick spot-check of another, functional ISO revealed that this root directory sector is supposed to be exact, not 1-indexed.

    Upon further inspection, I noticed that, while fuseiso appeared to work with that one patch, none of the files returned correct data, and none of the directories contained anything. That’s when I noticed that ALL of the sector locations described in the various directory and file records are off by 1 !

    Further Investigation

    I have occasionally run across ISO images on the Internet Archive that return the error about not being able to read the contents when trying to “View contents” (error text : “failed to obtain file list from xyz.iso”, as seen with this ISO). Too bad I didn’t make a record of them because I would be interested to see if they have the same corruption.

    Eventually, I’ll probably be able to compile an archive of deviant ISO-9660 images. A few months ago, I was processing a large collection from IA and found a corrupted ISO which had a cycle, i.e., the subdirectory pointed to a parent directory, which caused various ISO tools to loop forever. Just one of those things that is “never supposed to happen”, so why write code to deal with it gracefully ?

    See Also

    The post ISO-9660 Compromise, Part 2 : Finding Root first appeared on Breaking Eggs And Making Omelettes.

  • B2B Customer Journey Map : A Quickfire Guide for Growth

    20 mai 2024, par Erin

    What is a company’s biggest asset ?

    Its product ? Its employees ? Its unique selling proposition ?

    More and more people are recognising it’s something else entirely : your customers.

    Without your customers, your business can’t exist.

    Nearly 77% of B2B buyers found the buying process too complicated.

    With more competition than ever, it’s crucial you provide the best possible experience for them.

    That’s where your customer journey comes in.

    If you’re in the B2B space, you need to know how to map out the journey.

    By building a B2B customer journey map, you’ll be able to analyse the weak spots in the customer journey so you can improve the experience (and generate more revenue).

    In this article, we break down the B2B customer journey stages, how to build a customer journey map and how Matomo can help you track your customer journey automatically.

    What is a B2B customer journey ?

    Every customer goes through a specific path within your business.

    At some point in time, they found out about you and eventually bought your products.

    What is a B2B customer journey?

    A B2B customer journey is the collection of touchpoints your customer has with your business from start to finish.

    From discovery to purchase (and more), your customers go through a specific set of touches you can track. By analysing this journey, you can get a snapshot of your user experience.

    One way to track the customer journey is with a B2B customer journey map.

    It helps you to quickly see the different steps your customers take in their path with your business.

    With it, you can quickly identify weak spots and successes to improve the customer journey.

    5 stages of the B2B customer journey

    Every one of your customers is unique. Their specific needs and their journey.

    It’s all different.

    But, there are crucial steps they take through their journey as your customer.

    It’s the same path your entire customer base takes.

    Here are the five stages of the B2B customer journey (and why you should track them) :

    5 stages of the B2B customer journey.

    1. Awareness

    Awareness is the first stage that every B2B buyer goes through when they start their journey in B2B companies as a customer.

    At this stage, your target buyer understands they have a problem they need solving. They’re out, actively trying to solve this problem. 

    This is where you can stand out from the competition and give them a good first impression.

    Some helpful content you could create to do this is :

    • Blog posts
    • Social media posts
    • Ebooks
    • Whitepapers

    2. Consideration

    Next up, your buyer persona has an awareness of your company. But, now they’ve started narrowing down their options for potential businesses they’re interested in.

    They’ve selected yours as a potential business to hand their hard-earned cash over to, but they’re still making up their mind.

    At this point, you need to do what you can to clear up any objections and doubts in their mind and make them trust you.

    Some helpful content you could create here include :

    • Product demos by your sales team
    • Webinars
    • Case studies

    3. Conversion

    Next up, your target buyer has compared all their options and decided on you as the chosen product/company.

    This is where the purchase decision is made — when the B2B buyer actually signs or clicks “buy.”

    Here, you’ll want to provide more :

    • Case studies
    • Live demos
    • Customer service
    • Customer reviews/testimonials

    4. Loyalty

    Your B2B buyer is now a customer. But, not all customers return. The majority will slip away after the first purchase. If you want them to return, you need to fuel the relationship and nurture them even more.

    You’ll want to shift your efforts to nurturing the relationship with a post-purchase strategy where you build on that trust, seek customer feedback to prove high customer satisfaction and reward their loyalty.

    Some content you may want to create here includes :

    • Thank you emails
    • Follow-up emails
    • Follow-up calls
    • Product how-tos
    • Reward program
    • Surveys

    5. Advocacy

    The final stage of the B2B customer journey map is advocacy.

    This is the stage beyond loyalty where your customers aren’t just coming back for more ; they’re actively telling others about you.

    This is the cream of the crop when it comes to the B2B buyer stages, and it happens when you exceed customer expectations repeatedly.

    Your goal should be to eventually get all of your customers to this stage. Because then, they’re doing free marketing for you.

    This is only possible when a customer receives enough positive B2B customer experiences with your company where the value they’ve received far exceeds what they perceived they have given.

    Here are a few pieces of content you can create to fuel advocacy :

    • Surveys
    • Testimonial requests
    • Referral program

    Difference between B2C and B2B customer journeys

    Every person on earth who buys something enters the customer journey.

    But, not all customer journeys are created equal.

    This is especially true when you compare the B2C and B2B customer journeys.

    While there are similarities, the business-to-consumer (B2C) journey has clear differences compared to the business-to-business (B2B) journey.

    B2C vs. B2B customer journey.

    The most obvious difference between the two journeys is that B2B customer journeys are far more complex. 

    Not only are these two companies selling to different audiences, but they also have to deploy a completely different set of strategies to lead their customers down the path as far as they can go.

    While the journey structures are similar (from awareness to advocacy), there are differing motivating behaviours.

    Here’s a table showing the difference between B2C and B2B in the customer journey :

    Different FactorsB2BB2C
    Target audienceSmaller, industry more importantLarger, general consumer
    BuyerMultiple decision-makersOne decision-maker
    Buying decisionBased on needs of the organisation with multiple stakeholdersBased on an individual’s pain points
    Buying processMultiple stepsSingle step
    Customer retentionOrganisational needs and ROI-basedIndividual emotional factors
    Repeat sales driverDeep relationshipRepetition, attention-based

    Step-by-step guide to building a B2B customer journey map

    Now that you’ve got a basic understanding of the typical B2B customer journey, it’s time to build out your map so you can create a visual representation of the journey.

    Step-by-step guide to building a customer journey map.

    Here are six steps you need to take to craft an effective B2B customer journey map in your business :

    1. Identify your target audience (and different segments)

    The first step in customer journey mapping is to look at your target audience.

    You need to understand who they are and what different segments make up your audience.

    You need to look at the different roles each person plays within the journey.

    Unlike B2C, you’re not usually dealing with a single person. You likely have a few decision-makers you need to interact with to close a deal.

    The average B2B deal involves 6 to 10 people.

    Analyse the different roles and responsibilities of your audience.

    Figure out what requirements they need to onboard you. Understand each person’s level of influence in the buying decision.

    2. Determine your customers’ goals

    Now that you have a clear understanding of each person involved in the buying process, it’s time to analyse their unique needs and goals.

    Unlike B2C, which will include a single person with a single set of needs and goals, you have to look at several people through the decision-making process.

    What is every decision-maker’s goal ?

    An entry-level admin will have much different goals than a CEO.

    Understand each of their needs as it will be key to selling them and taking you to the next person in the chain of command.

    3. Lean on data and analytics

    Now it’s time to analyse your data.

    You don’t want to guess what will work on your B2B buyers. Instead, leverage data that proves what’s working (and what’s not).

    Analytics software like Matomo are crucial tools in your B2B customer journey toolkit.

    Matomo can help you make data-driven decisions to fuel customer acquisition and loyalty to help get more customers all the way to the advocacy stage.

    Using Matomo (which analyses and interprets different data sources) can give you a holistic view of what’s going on at each stage of the journey so you can reach your goals.

    Try Matomo for Free

    Get the web insights you need, without compromising data accuracy.

    No credit card required

    4. Draw out customer journey stages

    Now that you have your data-backed plan, it’s time for some customer journey mapping.

    You can do this on paper or use a diagram tool to create a visual B2B customer journey map.

    Here, you’ll draw out every single stage in your customer journey, including every single touchpoint from different decision-makers.

    5. Determine each customer touchpoint

    Once you’ve drawn up the customer journey stages, you’ll have a key list of B2B customer journey touchpoints to implement.

    Write down every single customer interaction possible on the journey through.

    This could be reading an email, a blog post or watching a video on your home page.

    It could be an advertisement, a phone call or a follow-up email.

    It could even be a live demo or video sales call (meeting).

    6. Identify your own goals

    Now that you’ve got your visual B2B customer journey mapping done, it’s time to go back to you and your company.

    What are your goals ?

    What are the end results you’re looking for here ?

    You’ve got your current map in place. Now, how would you like customers to go through this journey ?

    Where would you like them to end up ?

    Look back at your company’s primary objectives if you’re stuck here.

    If your company is looking to increase profit margins, then maybe you want to focus more on retention, so you’re spending less on acquisition (and leaning more on recurring revenue from existing customers).

    How to create a Matomo funnel to track your B2B customer journey

    If you want to start tracking and optimising your B2B customer journey, you need to have a good grasp on your funnel.

    The reality is that your customer journey is your funnel.

    They’re one and the same.

    Your customer journeys through your sales funnel.

    So, if you want to optimise it, then you need to see what’s going on at each stage of your funnel.

    Screenshot example of the Matomo dashboard

    With Matomo, you can map out your entire funnel and track key events like conversions.

    This allows you to identify where your site visitors are having problems, where they’re exiting and other obstacles they’re facing on their journey through.

    To start, you first define what events or touchpoints you want included. This could mean :

    • Landing on your website
    • Visiting a product page
    • Adding something to cart
    • Going to checkout
    • Clicking “buy”

    Then, at each stage, you’ll see conversion rates.

    For example, if only 3% of your visitors go from landing on your website to the product page, you likely have an issue between your homepage (and other pages) and your product pages.

    Or, if you can get people to add to cart, but you rarely get people going to checkout, there’s likely a problem to fix on your add-to-cart page.

    By leveraging Matomo’s funnels feature, you get to see your entire customer journey (and where people are falling off) so you understand what you need to optimise to grow your business.

    If you’re ready to start building and optimising your customer journey today, then try Matomo for free for 21 days.