summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtem Dyomin <artem.dyomin@qt.io>2023-07-31 17:40:01 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-08-02 12:36:23 +0000
commitbbf2b43855f971f07e461f70c364379c57f2fffe (patch)
treec4c094094dbb6b1b15661cebbc84b37ad0cfbcf8
parent2e5f1d3926cb6c6fdbbf5f86e10059e396c3604c (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.h2
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp68
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h6
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp16
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h2
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;