diff options
author | Artem Dyomin <artem.dyomin@qt.io> | 2023-07-31 17:40:01 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-08-02 12:36:23 +0000 |
commit | bbf2b43855f971f07e461f70c364379c57f2fffe (patch) | |
tree | c4c094094dbb6b1b15661cebbc84b37ad0cfbcf8 | |
parent | 2e5f1d3926cb6c6fdbbf5f86e10059e396c3604c (diff) |
Fix memory leak in audio encoder
2 objects were not deleted.
* resampler leaked anyway
* codec context might be deleted by ffmpeg,
the documentation recommends to delete it.
Since the variable was touched, it was renamed to proper name.
Change-Id: I94965e83b060b8db251b13c8c0d923cddf804741
Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
Reviewed-by: Jøger Hansegård <joger.hansegard@qt.io>
Reviewed-by: Pavel Dubsky <pavel.dubsky@qt.io>
(cherry picked from commit 9cda4c7a5906630d4a3306dae4cefd8331751685)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpeg_p.h | 2 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp | 68 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h | 6 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp | 16 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h | 2 |
5 files changed, 51 insertions, 43 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeg_p.h b/src/plugins/multimedia/ffmpeg/qffmpeg_p.h index 9c42f5693..2dc97c7e1 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpeg_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpeg_p.h @@ -135,6 +135,8 @@ using AVHWFramesConstraintsUPtr = std::unique_ptr< AVHWFramesConstraints, AVDeleter<decltype(&av_hwframe_constraints_free), &av_hwframe_constraints_free>>; +using SwrContextUPtr = std::unique_ptr<SwrContext, AVDeleter<decltype(&swr_free), &swr_free>>; + using PixelOrSampleFormat = int; using AVScore = int; constexpr AVScore BestAVScore = std::numeric_limits<AVScore>::max(); diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp index cd0e040bb..a9579c417 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp @@ -322,7 +322,7 @@ void AudioEncoder::open() { AVSampleFormat requested = QFFmpegMediaFormatInfo::avSampleFormat(format.sampleFormat()); - codec = avcodec_alloc_context3(avCodec); + codecContext.reset(avcodec_alloc_context3(avCodec)); if (stream->time_base.num != 1 || stream->time_base.den != format.sampleRate()) { qCDebug(qLcFFmpegEncoder) << "Most likely, av_format_write_header changed time base from" @@ -330,38 +330,43 @@ void AudioEncoder::open() << stream->time_base.num << "/" << stream->time_base.den; } - codec->time_base = stream->time_base; + codecContext->time_base = stream->time_base; - avcodec_parameters_to_context(codec, stream->codecpar); + avcodec_parameters_to_context(codecContext.get(), stream->codecpar); AVDictionaryHolder opts; - applyAudioEncoderOptions(settings, avCodec->name, codec, opts); + applyAudioEncoderOptions(settings, avCodec->name, codecContext.get(), opts); - int res = avcodec_open2(codec, avCodec, opts); + int res = avcodec_open2(codecContext.get(), avCodec, opts); qCDebug(qLcFFmpegEncoder) << "audio codec opened" << res; - qCDebug(qLcFFmpegEncoder) << "audio codec params: fmt=" << codec->sample_fmt << "rate=" << codec->sample_rate; + qCDebug(qLcFFmpegEncoder) << "audio codec params: fmt=" << codecContext->sample_fmt + << "rate=" << codecContext->sample_rate; - if (codec->sample_fmt != requested) { + if (codecContext->sample_fmt != requested) { + SwrContext *resampler = nullptr; #if QT_FFMPEG_OLD_CHANNEL_LAYOUT - resampler = swr_alloc_set_opts(nullptr, // we're allocating a new context - codec->channel_layout, // out_ch_layout - codec->sample_fmt, // out_sample_fmt - codec->sample_rate, // out_sample_rate - av_get_default_channel_layout(format.channelCount()), // in_ch_layout - requested, // in_sample_fmt - format.sampleRate(), // in_sample_rate - 0, // log_offset - nullptr); + resampler = swr_alloc_set_opts( + nullptr, // we're allocating a new context + codecContext->channel_layout, // out_ch_layout + codecContext->sample_fmt, // out_sample_fmt + codecContext->sample_rate, // out_sample_rate + av_get_default_channel_layout(format.channelCount()), // in_ch_layout + requested, // in_sample_fmt + format.sampleRate(), // in_sample_rate + 0, // log_offset + nullptr); #else AVChannelLayout in_ch_layout = {}; av_channel_layout_default(&in_ch_layout, format.channelCount()); - swr_alloc_set_opts2(&resampler, // we're allocating a new context - &codec->ch_layout, codec->sample_fmt, codec->sample_rate, - &in_ch_layout, requested, format.sampleRate(), - 0, nullptr); + swr_alloc_set_opts2(&resampler, // we're allocating a new context + &codecContext->ch_layout, codecContext->sample_fmt, + codecContext->sample_rate, &in_ch_layout, requested, + format.sampleRate(), 0, nullptr); #endif swr_init(resampler); + + this->resampler.reset(resampler); } } @@ -385,7 +390,7 @@ void AudioEncoder::init() { open(); if (input) { - input->setFrameSize(codec->frame_size); + input->setFrameSize(codecContext->frame_size); } qCDebug(qLcFFmpegEncoder) << "AudioEncoder::init started audio device thread."; } @@ -394,7 +399,7 @@ void AudioEncoder::cleanup() { while (!audioBufferQueue.empty()) loop(); - while (avcodec_send_frame(codec, nullptr) == AVERROR(EAGAIN)) + while (avcodec_send_frame(codecContext.get(), nullptr) == AVERROR(EAGAIN)) retrievePackets(); retrievePackets(); } @@ -409,7 +414,7 @@ void AudioEncoder::retrievePackets() { while (1) { AVPacketUPtr packet(av_packet_alloc()); - int ret = avcodec_receive_packet(codec, packet.get()); + int ret = avcodec_receive_packet(codecContext.get(), packet.get()); if (ret < 0) { if (ret != AVERROR(EOF)) break; @@ -437,28 +442,29 @@ void AudioEncoder::loop() retrievePackets(); auto frame = makeAVFrame(); - frame->format = codec->sample_fmt; + frame->format = codecContext->sample_fmt; #if QT_FFMPEG_OLD_CHANNEL_LAYOUT - frame->channel_layout = codec->channel_layout; - frame->channels = codec->channels; + frame->channel_layout = codecContext->channel_layout; + frame->channels = codecContext->channels; #else - frame->ch_layout = codec->ch_layout; + frame->ch_layout = codecContext->ch_layout; #endif - frame->sample_rate = codec->sample_rate; + frame->sample_rate = codecContext->sample_rate; frame->nb_samples = buffer.frameCount(); if (frame->nb_samples) av_frame_get_buffer(frame.get(), 0); if (resampler) { const uint8_t *data = buffer.constData<uint8_t>(); - swr_convert(resampler, frame->extended_data, frame->nb_samples, &data, frame->nb_samples); + swr_convert(resampler.get(), frame->extended_data, frame->nb_samples, &data, + frame->nb_samples); } else { memcpy(frame->buf[0]->data, buffer.constData<uint8_t>(), buffer.byteCount()); } const auto &timeBase = stream->time_base; const auto pts = timeBase.den && timeBase.num - ? timeBase.den * samplesWritten / (codec->sample_rate * timeBase.num) + ? timeBase.den * samplesWritten / (codecContext->sample_rate * timeBase.num) : samplesWritten; setAVFrameTime(*frame, pts, timeBase); samplesWritten += buffer.frameCount(); @@ -469,7 +475,7 @@ void AudioEncoder::loop() // qCDebug(qLcFFmpegEncoder) << "sending audio frame" << buffer.byteCount() << frame->pts << // ((double)buffer.frameCount()/frame->sample_rate); - int ret = avcodec_send_frame(codec, frame.get()); + int ret = avcodec_send_frame(codecContext.get(), frame.get()); if (ret < 0) { char errStr[1024]; av_strerror(ret, errStr, 1024); diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h index a2b64c09a..3e6306411 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h @@ -158,11 +158,11 @@ private: void loop() override; AVStream *stream = nullptr; - AVCodecContext *codec = nullptr; - QFFmpegAudioInput *input; + AVCodecContextUPtr codecContext; + QFFmpegAudioInput *input = nullptr; QAudioFormat format; - SwrContext *resampler = nullptr; + SwrContextUPtr resampler; qint64 samplesWritten = 0; const AVCodec *avCodec = nullptr; QMediaEncoderSettings settings; diff --git a/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp b/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp index 8b3c23f6c..f84aecfbc 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp @@ -33,6 +33,7 @@ Resampler::Resampler(const Codec *codec, const QAudioFormat &outputFormat) qCDebug(qLcResampler) << "init resampler" << m_outputFormat.sampleRate() << config << codecpar->sample_rate; + SwrContext *resampler = nullptr; #if QT_FFMPEG_OLD_CHANNEL_LAYOUT auto inConfig = codecpar->channel_layout; if (inConfig == 0) @@ -61,12 +62,10 @@ Resampler::Resampler(const Codec *codec, const QAudioFormat &outputFormat) nullptr); #endif swr_init(resampler); + m_resampler.reset(resampler); } -Resampler::~Resampler() -{ - swr_free(&resampler); -} +Resampler::~Resampler() = default; QAudioBuffer Resampler::resample(const AVFrame *frame) { @@ -75,7 +74,8 @@ QAudioBuffer Resampler::resample(const AVFrame *frame) QByteArray samples(m_outputFormat.bytesForFrames(maxOutSamples), Qt::Uninitialized); auto **in = const_cast<const uint8_t **>(frame->extended_data); auto *out = reinterpret_cast<uint8_t *>(samples.data()); - const int outSamples = swr_convert(resampler, &out, maxOutSamples, in, frame->nb_samples); + const int outSamples = + swr_convert(m_resampler.get(), &out, maxOutSamples, in, frame->nb_samples); samples.resize(m_outputFormat.bytesForFrames(outSamples)); @@ -89,7 +89,7 @@ QAudioBuffer Resampler::resample(const AVFrame *frame) int Resampler::adjustMaxOutSamples(const AVFrame *frame) { - int maxOutSamples = swr_get_out_samples(resampler, frame->nb_samples); + int maxOutSamples = swr_get_out_samples(m_resampler.get(), frame->nb_samples); const auto remainingCompensationDistance = m_endCompensationSample - m_samplesProcessed; @@ -101,7 +101,7 @@ int Resampler::adjustMaxOutSamples(const AVFrame *frame) // however it's not significant for our logic, in fact. // TODO: probably, it will need some improvements setSampleCompensation(0, 0); - maxOutSamples = swr_get_out_samples(resampler, frame->nb_samples); + maxOutSamples = swr_get_out_samples(m_resampler.get(), frame->nb_samples); } return maxOutSamples; @@ -109,7 +109,7 @@ int Resampler::adjustMaxOutSamples(const AVFrame *frame) void Resampler::setSampleCompensation(qint32 delta, quint32 distance) { - const int res = swr_set_compensation(resampler, delta, static_cast<int>(distance)); + const int res = swr_set_compensation(m_resampler.get(), delta, static_cast<int>(distance)); if (res < 0) qCWarning(qLcResampler) << "swr_set_compensation fail:" << res; else { diff --git a/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h b/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h index 3e67103b4..55802a17d 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h @@ -40,7 +40,7 @@ private: private: QAudioFormat m_outputFormat; - SwrContext *resampler = nullptr; + SwrContextUPtr m_resampler; qint64 m_samplesProcessed = 0; qint64 m_endCompensationSample = std::numeric_limits<qint64>::min(); qint32 m_sampleCompensationDelta = 0; |