Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • 53f310ab44649a4a268cb80d1192ae9d85ce08b8
  • master par défaut
  • script
  • new-devel
  • devel
  • timingView-edit
  • fix-mpv
7 résultats

Audio.cc

Blame
  • Audio.cc 6,46 Kio
    #include "Audio.hh"
    
    #include <QJsonObject>
    #include <QJsonArray>
    
    using namespace Vivy;
    
    // AudioContext class implementation
    
    // Create an audio contecxt from a file
    AudioContext::AudioContext(const QString &path)
        : Super(path)
    {
    }
    
    QString
    AudioContext::getElementName() const noexcept
    {
        return "Audio" + Super::getElementName();
    }
    
    QJsonDocument
    AudioContext::getProperties() const noexcept
    {
        return Super::getProperties();
    }
    
    // AudioStream class implementation
    
    // Constructor, need an AVFormat and an AVStream
    AudioStream::AudioStream(AVCodec *streamCodec, AVFormatContext *formatPtr, AVStream *streamArg,
                             int index)
        : Super(streamCodec, formatPtr, streamArg, index)
    {
        SwrContext *s = dataSwrContext.get();
        av_opt_set_int(s, "in_channel_count", codecContext->channels, 0);
        av_opt_set_int(s, "out_channel_count", 1, 0);
        av_opt_set_int(s, "in_channel_layout", static_cast<int64_t>(codecContext->channel_layout), 0);
        av_opt_set_int(s, "out_channel_layout", AV_CH_LAYOUT_MONO, 0);
        av_opt_set_int(s, "in_sample_rate", codecContext->sample_rate, 0);
        av_opt_set_int(s, "out_sample_rate", resamplerSampleRate, 0);
        av_opt_set_sample_fmt(s, "in_sample_fmt", codecContext->sample_fmt, 0);
        av_opt_set_sample_fmt(s, "out_sample_fmt", AV_SAMPLE_FMT_DBL, 0);
        swr_init(s);
        if (!swr_is_initialized(s))
            throw std::runtime_error("failed to initialize SwrContext resampler");
    }
    
    AudioStream::~AudioStream() noexcept
    {
        if (dataPtr) {
            qDebug() << "Free data ptr";
            free(dataPtr);
        }
        qDebug() << "Delete stream object";
    }
    
    QJsonObject
    AudioStream::getProperties() const noexcept
    {
        QJsonObject ret = Super::getProperties();
        ret.insert("Sample rate", codecParams->sample_rate);
        ret.insert("Bit rate", static_cast<int>(codecParams->bit_rate));
        ret.insert("Channels", codecParams->channels);
        return ret;
    }
    
    // Decode the data, don't call it if data already decoded
    void
    AudioStream::decodeData()
    {
        if (isDecoded())
            throw std::logic_error("audio stream is already resampled");
        qDebug() << "Launch decoding of stream" << streamIndexInContext;
    
        AVPacket packet;
        av_init_packet(&packet);
    
        // Iterate through frames
        while (av_read_frame(dataFormat, &packet) >= 0) {
            // Only decode audio
            if (packet.stream_index != streamIndexInContext) {
                av_packet_unref(&packet);
                continue;
            }
    
            // Decode one frame
            int response = avcodec_send_packet(codecContext.get(), &packet);
            if (response < 0) {
                throw std::runtime_error(
                    QStringLiteral("error n°%1 while sending a packet to the decoder")
                        .arg(response)
                        .toStdString());
            }
    
            double *buffer        = nullptr;
            int oldFrameNbSamples = 0;
    
            while (response >= 0) {
                response = avcodec_receive_frame(codecContext.get(), dataFrame.get());
                if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
                    break;
                } else if (response < 0) {
                    throw std::runtime_error(
                        QStringLiteral("error n°%1 while receiving a from from the decoder")
                            .arg(response)
                            .toStdString());
                }
    
                // Reuse buffer if possible
                if (oldFrameNbSamples < dataFrame->nb_samples) {
                    dataDeleter(buffer);
                    oldFrameNbSamples = dataFrame->nb_samples;
                    av_samples_alloc(reinterpret_cast<uint8_t **>(&buffer), nullptr, 1,
                                     dataFrame->nb_samples, AV_SAMPLE_FMT_DBL, 0);
                }
    
                // Resample frame
                if (const int frame_count_int = swr_convert(
                        dataSwrContext.get(), reinterpret_cast<uint8_t **>(&buffer),
                        dataFrame->nb_samples,
                        const_cast<const uint8_t **>(reinterpret_cast<uint8_t **>(dataFrame->data)),
                        dataFrame->nb_samples);
                    frame_count_int < 0) {
                    throw std::runtime_error("error on frame count, is negative but should not be");
                }
    
                // Append resampled frames to data
                else {
                    const size_t frame_count = static_cast<size_t>(frame_count_int);
                    dataPtr                  = reinterpret_cast<double *>(
                        realloc(dataPtr, (dataSize + static_cast<size_t>(dataFrame->nb_samples)) *
                                             sizeof(double)));
                    memcpy(dataPtr + dataSize, buffer, frame_count * sizeof(double));
                    dataSize += frame_count;
                }
            }
    
            dataDeleter(buffer);
            av_packet_unref(&packet);
        }
    
        qDebug() << "Decoding data finished for stream" << streamIndexInContext
                 << "dataPtr =" << dataPtr << "with dataSize =" << dataSize;
    }
    
    // Delete decoded data, clean up thing
    void
    AudioStream::cleanUpData() noexcept
    {
        free(dataPtr);
        dataPtr  = nullptr;
        dataSize = 0;
    }
    
    // Get the number of channels in the audio context stream
    int
    AudioStream::getChannels() const noexcept
    {
        return codecContext->channels;
    }
    
    // Get the sample rate
    int
    AudioStream::getSampleRate() const noexcept
    {
        return codecContext->sample_rate;
    }
    
    // Get the sample rate of the decoded data (need to call decodeData for that
    // value to have a meaning).
    int
    AudioStream::getDataSampleRate() const noexcept
    {
        return resamplerSampleRate;
    }
    
    // Get the bit rate of the stream
    qint64
    AudioStream::getBitRate() const noexcept
    {
        return codecContext->bit_rate;
    }
    
    // Get the information about the decoded state of this stream
    bool
    AudioStream::isDecoded() const noexcept
    {
        return dataPtr != nullptr;
    }
    
    // Get the decoded data's size, will return 0 if the data is not decoded. Don't
    // rely on this behaviour to get the decoded state of the stream!
    size_t
    AudioStream::getDecodedDataSize() const noexcept
    {
        return dataSize;
    }
    
    // Get the decoded data, safe to call even if the data is not decoded has the
    // pointer will be null. You must check it with an `if` statement.
    double *
    AudioStream::getDecodedData() const noexcept
    {
        return dataPtr;
    }
    
    // Get the chunk size of the decoded data
    size_t
    AudioStream::getDecodedChunkSize() const noexcept
    {
        return 512;
    }
    
    // Get the decalage of the decoded data
    size_t
    AudioStream::getDecodedDecalage() const noexcept
    {
        constexpr size_t overlap = 128; // The overlap
        return getDecodedChunkSize() - overlap;
    }