summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJan Holthuis <jan.holthuis@ruhr-uni-bochum.de>2021-09-04 00:03:18 +0200
committerGitHub <noreply@github.com>2021-09-04 00:03:18 +0200
commit40085a377fd4f2ca22d7b32a7ac874b0dea674f6 (patch)
tree9ccdfef1cbad16de152d823334ae05b678a8aebf /src
parent6cd39a505c4ee101a5df9a6462d70b57c7950edd (diff)
parenta509808001e9465a55575c2dc2ea301bab5f09d3 (diff)
Merge pull request #4245 from uklotzde/lp1934785-soundsourceffmpeg
lp1934785 SoundSourceFFmpeg: Ignore inaudible samples before start of stream
Diffstat (limited to 'src')
-rw-r--r--src/sources/readaheadframebuffer.cpp19
-rw-r--r--src/sources/soundsourceffmpeg.cpp35
2 files changed, 50 insertions, 4 deletions
diff --git a/src/sources/readaheadframebuffer.cpp b/src/sources/readaheadframebuffer.cpp
index 407074963c..f2db73a920 100644
--- a/src/sources/readaheadframebuffer.cpp
+++ b/src/sources/readaheadframebuffer.cpp
@@ -3,10 +3,17 @@
#include "util/logger.h"
#include "util/sample.h"
+// Override or set to `true` to enable verbose debug logging.
#if !defined(VERBOSE_DEBUG_LOG)
#define VERBOSE_DEBUG_LOG false
#endif
+// Override or set to `true` to break with a debug assertion
+// if an overlap or gap in the audio stream has been detected.
+#if !defined(DEBUG_ASSERT_ON_DISCONTINUITIES)
+#define DEBUG_ASSERT_ON_DISCONTINUITIES false
+#endif
+
namespace mixxx {
namespace {
@@ -135,6 +142,9 @@ ReadableSampleFrames ReadAheadFrameBuffer::fillBuffer(
<< bufferedRange()
<< "and input buffer"
<< inputRange;
+#if DEBUG_ASSERT_ON_DISCONTINUITIES
+ DEBUG_ASSERT(!"Unexpected gap");
+#endif
switch (discontinuityGapMode) {
case DiscontinuityGapMode::Skip:
reset(inputRange.start());
@@ -314,6 +324,9 @@ WritableSampleFrames ReadAheadFrameBuffer::consumeAndFillBuffer(
<< outputRange
<< "with input buffer"
<< inputRange;
+#if DEBUG_ASSERT_ON_DISCONTINUITIES
+ DEBUG_ASSERT(!"Unexpected overlap");
+#endif
switch (discontinuityOverlapMode) {
case DiscontinuityOverlapMode::Ignore:
break;
@@ -345,6 +358,9 @@ WritableSampleFrames ReadAheadFrameBuffer::consumeAndFillBuffer(
<< bufferedRange()
<< "with input buffer"
<< inputRange;
+#if DEBUG_ASSERT_ON_DISCONTINUITIES
+ DEBUG_ASSERT(!"Unexpected overlap");
+#endif
switch (discontinuityOverlapMode) {
case DiscontinuityOverlapMode::Ignore:
break;
@@ -397,6 +413,9 @@ WritableSampleFrames ReadAheadFrameBuffer::consumeAndFillBuffer(
<< outputRange
<< "and input buffer"
<< inputRange;
+#if DEBUG_ASSERT_ON_DISCONTINUITIES
+ DEBUG_ASSERT(!"Unexpected gap");
+#endif
switch (discontinuityGapMode) {
case DiscontinuityGapMode::Skip:
break;
diff --git a/src/sources/soundsourceffmpeg.cpp b/src/sources/soundsourceffmpeg.cpp
index d76851f585..e1b48196b2 100644
--- a/src/sources/soundsourceffmpeg.cpp
+++ b/src/sources/soundsourceffmpeg.cpp
@@ -60,7 +60,7 @@ constexpr int64_t kavStreamDecoderFrameDelayAAC = 2112;
// Use 0-based sample frame indexing
constexpr SINT kMinFrameIndex = 0;
-constexpr SINT kSamplesPerMP3Frame = 1152;
+constexpr SINT kMaxSamplesPerMP3Frame = 1152;
const Logger kLogger("SoundSourceFFmpeg");
@@ -106,7 +106,7 @@ inline int64_t getStreamStartTime(const AVStream& avStream) {
// using the default start time.
// Not all M4A files encode the start_time correctly, e.g.
// the test file cover-test-itunes-12.7.0-aac.m4a has a valid
- // start_time of 0. Unfortunately, this special case is cannot
+ // start_time of 0. Unfortunately, this special case cannot be
// detected and compensated.
start_time = math_max(kavStreamDefaultStartTime, kavStreamDecoderFrameDelayAAC);
break;
@@ -135,6 +135,7 @@ inline int64_t getStreamEndTime(const AVStream& avStream) {
}
inline SINT convertStreamTimeToFrameIndex(const AVStream& avStream, int64_t pts) {
+ DEBUG_ASSERT(pts != AV_NOPTS_VALUE);
// getStreamStartTime(avStream) -> 1st audible frame at kMinFrameIndex
return kMinFrameIndex +
av_rescale_q(
@@ -185,7 +186,7 @@ SINT getStreamSeekPrerollFrameCount(const AVStream& avStream) {
// slight deviations from the exact signal!
DEBUG_ASSERT(avStream.codecpar->channels <= 2);
const SINT mp3SeekPrerollFrameCount =
- 9 * (kSamplesPerMP3Frame / avStream.codecpar->channels);
+ 9 * (kMaxSamplesPerMP3Frame / avStream.codecpar->channels);
return math_max(mp3SeekPrerollFrameCount, defaultSeekPrerollFrameCount);
}
case AV_CODEC_ID_AAC:
@@ -1051,12 +1052,33 @@ ReadableSampleFrames SoundSourceFFmpeg::readSampleFramesClamped(
#if VERBOSE_DEBUG_LOG
avTrace("Received decoded frame", *m_pavDecodedFrame);
#endif
- DEBUG_ASSERT(m_pavDecodedFrame->pts != AV_NOPTS_VALUE);
+ VERIFY_OR_DEBUG_ASSERT(
+ (m_pavDecodedFrame->flags &
+ (AV_FRAME_FLAG_CORRUPT |
+ AV_FRAME_FLAG_DISCARD)) == 0) {
+ av_frame_unref(m_pavDecodedFrame);
+ continue;
+ }
const auto decodedFrameCount = m_pavDecodedFrame->nb_samples;
DEBUG_ASSERT(decodedFrameCount > 0);
auto streamFrameIndex =
convertStreamTimeToFrameIndex(
*m_pavStream, m_pavDecodedFrame->pts);
+ // Only audible samples are counted, i.e. any inaudible aka
+ // "priming" samples are not included in nb_samples!
+ // https://bugs.launchpad.net/mixxx/+bug/1934785
+ if (streamFrameIndex < kMinFrameIndex) {
+#if VERBOSE_DEBUG_LOG
+ const auto inaudibleFrameCountUntilStartOfStream =
+ kMinFrameIndex - streamFrameIndex;
+ kLogger.debug()
+ << "Skipping"
+ << inaudibleFrameCountUntilStartOfStream
+ << "inaudible sample frames before the start of the stream";
+#endif
+ streamFrameIndex = kMinFrameIndex;
+ }
+ DEBUG_ASSERT(streamFrameIndex >= kMinFrameIndex);
decodedFrameRange = IndexRange::forward(
streamFrameIndex,
decodedFrameCount);
@@ -1203,6 +1225,11 @@ ReadableSampleFrames SoundSourceFFmpeg::readSampleFramesClamped(
// Housekeeping before next decoding iteration
av_frame_unref(m_pavDecodedFrame);
av_frame_unref(m_pavResampledFrame);
+
+ // The first loop condition (see below) should always be true
+ // and has only been added to prevent infinite looping in case
+ // of unexpected result values.
+ DEBUG_ASSERT(avcodec_receive_frame_result == 0);
} while (avcodec_receive_frame_result == 0 &&
m_frameBuffer.isValid());
}