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
Branches
No related tags found
No related merge requests found
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
* Author: Alexandre Savard <alexandre.savard@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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -21,11 +22,7 @@ ...@@ -21,11 +22,7 @@
#include "libav_deps.h" #include "libav_deps.h"
#include "logger.h" #include "logger.h"
#include "media_buffer.h"
#include "media_filter.h"
#include "media_stream.h"
#include "resampler.h" #include "resampler.h"
#include "ring_types.h"
extern "C" { extern "C" {
#include <libswresample/swresample.h> #include <libswresample/swresample.h>
...@@ -35,7 +32,7 @@ namespace ring { ...@@ -35,7 +32,7 @@ namespace ring {
Resampler::Resampler() Resampler::Resampler()
: swrCtx_(swr_alloc()) : swrCtx_(swr_alloc())
, initialized_(false) , initCount_(0)
{} {}
Resampler::~Resampler() Resampler::~Resampler()
...@@ -57,17 +54,25 @@ Resampler::reinit(const AVFrame* in, const AVFrame* out) ...@@ -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); av_opt_set_sample_fmt(swrCtx_, "osf", static_cast<AVSampleFormat>(out->format), 0);
swr_init(swrCtx_); swr_init(swrCtx_);
initialized_ = true; ++initCount_;
} }
int int
Resampler::resample(const AVFrame* input, AVFrame* output) Resampler::resample(const AVFrame* input, AVFrame* output)
{ {
if (!initialized_) if (!initCount_)
reinit(input, output); reinit(input, output);
int ret = swr_convert_frame(swrCtx_, output, input); int ret = swr_convert_frame(swrCtx_, output, input);
if (ret & AVERROR_INPUT_CHANGED || ret & AVERROR_OUTPUT_CHANGED) { 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); reinit(input, output);
return resample(input, output); return resample(input, output);
} else if (ret < 0) { } else if (ret < 0) {
...@@ -75,6 +80,8 @@ Resampler::resample(const AVFrame* input, AVFrame* output) ...@@ -75,6 +80,8 @@ Resampler::resample(const AVFrame* input, AVFrame* output)
return -1; return -1;
} }
// Resampling worked, reset count to 1 so reinit isn't called again
initCount_ = 1;
return 0; return 0;
} }
...@@ -126,5 +133,4 @@ Resampler::resample(std::shared_ptr<AudioFrame>&& in, const AudioFormat& format) ...@@ -126,5 +133,4 @@ Resampler::resample(std::shared_ptr<AudioFrame>&& in, const AudioFormat& format)
return output; return output;
} }
} // namespace ring } // namespace ring
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
* Author: Alexandre Savard <alexandre.savard@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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -22,8 +23,8 @@ ...@@ -22,8 +23,8 @@
#pragma once #pragma once
#include "audiobuffer.h" #include "audiobuffer.h"
#include "media_buffer.h"
#include "noncopyable.h" #include "noncopyable.h"
#include "ring_types.h"
struct AVFrame; struct AVFrame;
struct SwrContext; struct SwrContext;
...@@ -45,12 +46,9 @@ class Resampler { ...@@ -45,12 +46,9 @@ class Resampler {
int resample(const AVFrame* input, AVFrame* output); int resample(const AVFrame* input, AVFrame* output);
/** /**
* Resample from @dataIn format to @dataOut format. * Wrappers around resample(AVFrame*, AVFrame*) for convenience.
*
* NOTE: This is a wrapper for resample(AVFrame*, AVFrame*)
*/ */
void resample(const AudioBuffer& dataIn, AudioBuffer& dataOut); void resample(const AudioBuffer& dataIn, AudioBuffer& dataOut);
std::unique_ptr<AudioFrame> resample(std::unique_ptr<AudioFrame>&& in, const AudioFormat& out); 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); std::shared_ptr<AudioFrame> resample(std::shared_ptr<AudioFrame>&& in, const AudioFormat& out);
...@@ -59,13 +57,25 @@ class Resampler { ...@@ -59,13 +57,25 @@ class Resampler {
/** /**
* Reinitializes the resampler when new settings are detected. As long as both input and * 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 * output formats don't change, this will only be called once.
* initialization is done in swr_convert_frame.
*/ */
void reinit(const AVFrame* in, const AVFrame* out); 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 } // namespace ring
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment