From a0326abc0b270b0736092e240d93cb10217ab736 Mon Sep 17 00:00:00 2001 From: Sergiu Ciumac Date: Thu, 30 Nov 2023 12:08:42 +0200 Subject: [PATCH 1/4] Wording. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 634b2d40..18df5db0 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Read [Supported Media Formats][audio-services-wiki-page] page for details about Since `v8.0.0` video fingerprinting support has been added. Similarly to audio fingerprinting, video fingerprints are generated from video frames, and used to insert and later query the datastore for exact and similar matches. You can use `SoundFingerprinting` to fingerprint either audio or video content or both at the same time. More details about video fingerprinting are available [here][video-fingerprinting-wiki-page]. ### Version 9 -Version 9 was released to accomodate `SoundFingerprinting.Emy` v9.0.0, which upgrades to FFmpeg v5.x (breaking change as prior versions are using FFmpeg v4.x. +Version 9 was released to accomodate `SoundFingerprinting.Emy` v9.0.0, which upgrades to FFmpeg v5.x (breaking change as v8.x is using FFmpeg v4.x). If you are not using `SoundFingerprinting.Emy` you can safely upgrade to v9. From ebc0850955087b27d6a9a90cc23db1902a4e86e8 Mon Sep 17 00:00:00 2001 From: Sergiu Ciumac Date: Thu, 28 Dec 2023 10:07:19 +0200 Subject: [PATCH 2/4] Fixing realtime query source failures to generate audio samples should not result in double AV result entry registration because of a purge operation that is followed by query source continuation. --- .../Unit/Query/RealtimeQueryCommandTest.cs | 51 +++++++++++++++++++ .../Command/RealtimeQueryCommand.cs | 29 +++++++---- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/SoundFingerprinting.Tests/Unit/Query/RealtimeQueryCommandTest.cs b/src/SoundFingerprinting.Tests/Unit/Query/RealtimeQueryCommandTest.cs index e3302531..a8cbaf34 100644 --- a/src/SoundFingerprinting.Tests/Unit/Query/RealtimeQueryCommandTest.cs +++ b/src/SoundFingerprinting.Tests/Unit/Query/RealtimeQueryCommandTest.cs @@ -708,6 +708,57 @@ await QueryCommandBuilder.Instance Assert.IsTrue(errored); } + [Test] + public async Task ShouldHandleExceptionInRealtimeSource() + { + var modelService = new InMemoryModelService(); + + int count = 20; + var data = GenerateRandomAudioChunks(count, 1, DateTime.UtcNow); + var concatenated = Concatenate(data); + var hashes = await FingerprintCommandBuilder.Instance + .BuildFingerprintCommand() + .From(concatenated) + .WithFingerprintConfig(config => config) + .Hash(); + + modelService.Insert(new TrackInfo("312", "Bohemian Rhapsody", "Queen"), hashes); + + var collection = SimulateRealtimeAudioQueryData(data, jitterLength: 0); + var withFailure = SimulateFailure(collection, throwExceptionAfter: 15); + + var success = new List(); + var didNotPass = new List(); + await QueryCommandBuilder.Instance.BuildRealtimeQueryCommand() + .From(withFailure) + .WithRealtimeQueryConfig(config => + { + config.ResultEntryFilter = new TrackRelativeCoverageLengthEntryFilter(coverage: 0.2, waitTillCompletion: true); + config.SuccessCallback = _ => success.AddRange(_.ResultEntries); + config.DidNotPassFilterCallback = _ => didNotPass.AddRange(_.ResultEntries); + config.ErrorCallback = (Action)((exception, _) => logger.LogError(exception, "An error occured while querying stream")); + return config; + }) + .UsingServices(modelService) + .Query(CancellationToken.None); + + Assert.That(success.Count, Is.EqualTo(1)); + Assert.That(didNotPass.Count, Is.Zero); + } + + private static async IAsyncEnumerable SimulateFailure(IAsyncEnumerable collection, int throwExceptionAfter) + { + await foreach (var audioSamples in collection) + { + if (throwExceptionAfter-- == 0) + { + throw new Exception("Error while reading samples from the source."); + } + + yield return audioSamples; + } + } + private static async IAsyncEnumerable GetSamples(int count, int seconds, [EnumeratorCancellation] CancellationToken cancellationToken, int sampleRate = 5512, int delay = 0) { foreach (var track in Enumerable.Range(0, count).Select(_ => new AVTrack(new AudioTrack(TestUtilities.GenerateRandomAudioSamples(seconds * sampleRate, sampleRate: sampleRate)), null))) diff --git a/src/SoundFingerprinting/Command/RealtimeQueryCommand.cs b/src/SoundFingerprinting/Command/RealtimeQueryCommand.cs index 2b35e9c9..b9447f36 100644 --- a/src/SoundFingerprinting/Command/RealtimeQueryCommand.cs +++ b/src/SoundFingerprinting/Command/RealtimeQueryCommand.cs @@ -285,28 +285,39 @@ private async Task QueryRealtimeSource(CancellationToken cancellationTok { await QueryFromRealtimeAndOffline(resultsAggregator, cancellationToken); logger.LogInformation("Stopped querying realtime source after {QueryLength:00} seconds", queryLength); + PurgeResultEntryAggregator(resultsAggregator); return queryLength; } catch (Exception e) when (e is OperationCanceledException or ObjectDisposedException) { + PurgeResultEntryAggregator(resultsAggregator); throw; } catch (Exception e) { - // here we catch exceptions that occur while reading from the realtime source - HandleQueryFailure(null, e); + try + { + // here we catch exceptions that occur while reading from the realtime source + HandleQueryFailure(null, e); + } + catch (Exception) + { + PurgeResultEntryAggregator(resultsAggregator); + throw; + } + await Task.Delay(configuration.ErrorBackoffPolicy.RemainingDelay, cancellationToken); } - finally - { - // let's purge stateful results aggregator to safe-guard ourselves from memory issues when certain tracks get stuck in the aggregator - var purged = resultsAggregator.Purge(); - InvokeCallbackHandler(purged.SuccessEntries, configuration?.SuccessCallback); - InvokeCallbackHandler(purged.DidNotPassThresholdEntries, configuration?.DidNotPassFilterCallback); - } } } + private void PurgeResultEntryAggregator(IRealtimeResultEntryAggregator resultsAggregator) + { + var purged = resultsAggregator.Purge(); + InvokeCallbackHandler(purged.SuccessEntries, configuration?.SuccessCallback); + InvokeCallbackHandler(purged.DidNotPassThresholdEntries, configuration?.DidNotPassFilterCallback); + } + /// /// Query from realtime and offline sources. /// From fd48db48af310d59a9a082a653060135ca10c9c2 Mon Sep 17 00:00:00 2001 From: Sergiu Ciumac Date: Thu, 28 Dec 2023 10:40:22 +0200 Subject: [PATCH 3/4] More logging messages. --- src/SoundFingerprinting/Command/RealtimeQueryCommand.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SoundFingerprinting/Command/RealtimeQueryCommand.cs b/src/SoundFingerprinting/Command/RealtimeQueryCommand.cs index b9447f36..b9d55df5 100644 --- a/src/SoundFingerprinting/Command/RealtimeQueryCommand.cs +++ b/src/SoundFingerprinting/Command/RealtimeQueryCommand.cs @@ -300,8 +300,9 @@ private async Task QueryRealtimeSource(CancellationToken cancellationTok // here we catch exceptions that occur while reading from the realtime source HandleQueryFailure(null, e); } - catch (Exception) + catch (Exception e2) { + logger.LogWarning(e2, "Failed to handle query failure. Purging realtime aggregator, exiting query loop"); PurgeResultEntryAggregator(resultsAggregator); throw; } From f369f3a37bf76363f491b0f45908c82567392174 Mon Sep 17 00:00:00 2001 From: Sergiu Ciumac Date: Thu, 28 Dec 2023 10:48:39 +0200 Subject: [PATCH 4/4] Version bump. Adding release notes. --- src/SoundFingerprinting.Tests/Properties/AssemblyInfo.cs | 4 ++-- src/SoundFingerprinting/Properties/AssemblyInfo.cs | 4 ++-- src/SoundFingerprinting/SoundFingerprinting.csproj | 7 +++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/SoundFingerprinting.Tests/Properties/AssemblyInfo.cs b/src/SoundFingerprinting.Tests/Properties/AssemblyInfo.cs index a21b33fd..a24bba3c 100644 --- a/src/SoundFingerprinting.Tests/Properties/AssemblyInfo.cs +++ b/src/SoundFingerprinting.Tests/Properties/AssemblyInfo.cs @@ -11,5 +11,5 @@ [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("4cac962e-ebc5-4006-a1e0-7ffb3e2483c2")] -[assembly: AssemblyVersion("9.0.0.101")] -[assembly: AssemblyInformationalVersion("9.0.0.101")] +[assembly: AssemblyVersion("9.1.0.100")] +[assembly: AssemblyInformationalVersion("9.1.0.100")] diff --git a/src/SoundFingerprinting/Properties/AssemblyInfo.cs b/src/SoundFingerprinting/Properties/AssemblyInfo.cs index 45b659b2..61177248 100644 --- a/src/SoundFingerprinting/Properties/AssemblyInfo.cs +++ b/src/SoundFingerprinting/Properties/AssemblyInfo.cs @@ -19,5 +19,5 @@ [assembly: InternalsVisibleTo("SoundFingerprinting.FFT.FFTW")] [assembly: InternalsVisibleTo("SoundFingerprinting.FFT.FFTW.Tests")] -[assembly: AssemblyVersion("9.0.0.101")] -[assembly: AssemblyInformationalVersion("9.0.0.101")] +[assembly: AssemblyVersion("9.1.0.100")] +[assembly: AssemblyInformationalVersion("9.1.0.100")] diff --git a/src/SoundFingerprinting/SoundFingerprinting.csproj b/src/SoundFingerprinting/SoundFingerprinting.csproj index 904eb9bd..cadab374 100644 --- a/src/SoundFingerprinting/SoundFingerprinting.csproj +++ b/src/SoundFingerprinting/SoundFingerprinting.csproj @@ -4,16 +4,15 @@ true false enable - 9.0.0 + 9.1.0 Sergiu Ciumac SoundFingerprinting is a C# framework that implements an efficient algorithm of audio fingerprinting and identification. Designed for developers, enthusiasts, researchers in the fields of audio processing, data mining, digital signal processing. https://github.com/addictedcs/soundfingerprinting https://github.com/AddictedCS/soundfingerprinting git - Version bump to v9.0.0 accomodating SoundFingerprinting.Emy version upgrade. - If you are using SoundFingerprinting.Emy, please upgrade your FFmpeg library to v5. FFmpeg v4 will not be working with SoundFingerprinting.Emy v9.0.0. - If you are not using SoundFingerprinting.Emy, you can safely ignore this message. + Version bump to v9.1.0. + Realtime result entry aggregator may have been registering duplicate matches, during an error that happened while reading from realtime media service. This is now fixed. Audio Video Identification Fingerprinting Digital Signal Processing Music Recognition Data Mining Content Sound Shazam latest