Skip to content
Snippets Groups Projects
Commit bc594b5d authored by Philippe Gorley's avatar Philippe Gorley Committed by Philippe Gorley
Browse files

resampler: detect infinite loops

Under certain conditions, the resampler reinitializes itself in an
infinite loop. Prevent this by detecting this state and throwing an
exception.

This is not normal behaviour and is indicative of an underlying bug.

Change-Id: I70d58465daad12645d6bb88833e62e1232891e3b
parent 123703b4
No related branches found
No related tags found
No related merge requests found
......@@ -3,6 +3,7 @@
*
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
* Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -21,11 +22,7 @@
#include "libav_deps.h"
#include "logger.h"
#include "media_buffer.h"
#include "media_filter.h"
#include "media_stream.h"
#include "resampler.h"
#include "ring_types.h"
extern "C" {
#include <libswresample/swresample.h>
......@@ -35,7 +32,7 @@ namespace ring {
Resampler::Resampler()
: swrCtx_(swr_alloc())
, initialized_(false)
, initCount_(0)
{}
Resampler::~Resampler()
......@@ -57,17 +54,25 @@ Resampler::reinit(const AVFrame* in, const AVFrame* out)
av_opt_set_sample_fmt(swrCtx_, "osf", static_cast<AVSampleFormat>(out->format), 0);
swr_init(swrCtx_);
initialized_ = true;
++initCount_;
}
int
Resampler::resample(const AVFrame* input, AVFrame* output)
{
if (!initialized_)
if (!initCount_)
reinit(input, output);
int ret = swr_convert_frame(swrCtx_, output, input);
if (ret & AVERROR_INPUT_CHANGED || ret & AVERROR_OUTPUT_CHANGED) {
// Under certain conditions, the resampler reinits itself in an infinite loop. This is
// indicative of an underlying problem in the code. This check is so the backtrace
// doesn't get mangled with a bunch of calls to Resampler::resample
if (initCount_ > 1) {
std::string msg = "Infinite loop detected in audio resampler, please open an issue on https://git.jami.net";
RING_ERR() << msg;
throw std::runtime_error(msg);
}
reinit(input, output);
return resample(input, output);
} else if (ret < 0) {
......@@ -75,6 +80,8 @@ Resampler::resample(const AVFrame* input, AVFrame* output)
return -1;
}
// Resampling worked, reset count to 1 so reinit isn't called again
initCount_ = 1;
return 0;
}
......@@ -126,5 +133,4 @@ Resampler::resample(std::shared_ptr<AudioFrame>&& in, const AudioFormat& format)
return output;
}
} // namespace ring
......@@ -3,6 +3,7 @@
*
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
* Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -22,8 +23,8 @@
#pragma once
#include "audiobuffer.h"
#include "media_buffer.h"
#include "noncopyable.h"
#include "ring_types.h"
struct AVFrame;
struct SwrContext;
......@@ -45,12 +46,9 @@ class Resampler {
int resample(const AVFrame* input, AVFrame* output);
/**
* Resample from @dataIn format to @dataOut format.
*
* NOTE: This is a wrapper for resample(AVFrame*, AVFrame*)
* Wrappers around resample(AVFrame*, AVFrame*) for convenience.
*/
void resample(const AudioBuffer& dataIn, AudioBuffer& dataOut);
std::unique_ptr<AudioFrame> resample(std::unique_ptr<AudioFrame>&& in, const AudioFormat& out);
std::shared_ptr<AudioFrame> resample(std::shared_ptr<AudioFrame>&& in, const AudioFormat& out);
......@@ -59,13 +57,25 @@ class Resampler {
/**
* Reinitializes the resampler when new settings are detected. As long as both input and
* output buffers always have the same formats, will never be called, as the first
* initialization is done in swr_convert_frame.
* output formats don't change, this will only be called once.
*/
void reinit(const AVFrame* in, const AVFrame* out);
SwrContext* swrCtx_; // incomplete type, cannot be a unique_ptr
bool initialized_;
/**
* Libswresample resampler context.
*
* NOTE SwrContext is an imcomplete type and cannot be stored in a smart pointer.
*/
SwrContext* swrCtx_;
/**
* Number of times @swrCtx_ has been initialized with no successful audio resampling.
*
* 0: Uninitialized
* 1: Initialized
* >1: Invalid frames or formats, reinit is going to be called in an infinite loop
*/
unsigned initCount_;
};
} // namespace ring
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment