bestsource: Add audio provider
This commit is contained in:
parent
70fd08aabe
commit
6af0b41f47
10 changed files with 220 additions and 25 deletions
|
@ -226,6 +226,7 @@ if get_option('bestsource').enabled()
|
||||||
conf.set('WITH_BESTSOURCE', 1)
|
conf.set('WITH_BESTSOURCE', 1)
|
||||||
bs = subproject('bestsource')
|
bs = subproject('bestsource')
|
||||||
deps += bs.get_variable('bestsource_dep')
|
deps += bs.get_variable('bestsource_dep')
|
||||||
|
dep_avail += 'BestSource'
|
||||||
needs_ffmpeg = true
|
needs_ffmpeg = true
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
118
src/audio_provider_bestsource.cpp
Normal file
118
src/audio_provider_bestsource.cpp
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
// Copyright (c) 2022, arch1t3cht <arch1t3cht@gmail.com>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
/// @file audio_provider_bestsource.cpp
|
||||||
|
/// @brief BS-based audio provider
|
||||||
|
/// @ingroup audio_input bestsource
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifdef WITH_BESTSOURCE
|
||||||
|
#include <libaegisub/audio/provider.h>
|
||||||
|
|
||||||
|
#include "audiosource.h"
|
||||||
|
|
||||||
|
#include "bestsource_common.h"
|
||||||
|
#include "compat.h"
|
||||||
|
#include "options.h"
|
||||||
|
|
||||||
|
#include <libaegisub/fs.h>
|
||||||
|
#include <libaegisub/make_unique.h>
|
||||||
|
#include <libaegisub/background_runner.h>
|
||||||
|
#include <libaegisub/log.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class BSAudioProvider final : public agi::AudioProvider {
|
||||||
|
std::map<std::string, std::string> bsopts;
|
||||||
|
BestAudioSource bs;
|
||||||
|
AudioProperties properties;
|
||||||
|
|
||||||
|
void FillBuffer(void *Buf, int64_t Start, int64_t Count) const override;
|
||||||
|
public:
|
||||||
|
BSAudioProvider(agi::fs::path const& filename, agi::BackgroundRunner *br);
|
||||||
|
|
||||||
|
bool NeedsCache() const override { return OPT_GET("Provider/Audio/BestSource/Aegisub Cache")->GetBool(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Constructor
|
||||||
|
/// @param filename The filename to open
|
||||||
|
BSAudioProvider::BSAudioProvider(agi::fs::path const& filename, agi::BackgroundRunner *br) try
|
||||||
|
: bsopts()
|
||||||
|
, bs(filename.string(), -1, -1, GetBSCacheFile(filename), &bsopts)
|
||||||
|
{
|
||||||
|
bs.SetMaxCacheSize(OPT_GET("Provider/Audio/BestSource/Max Cache Size")->GetInt() << 20);
|
||||||
|
br->Run([&](agi::ProgressSink *ps) {
|
||||||
|
ps->SetTitle(from_wx(_("Exacting")));
|
||||||
|
ps->SetMessage(from_wx(_("Creating cache... This can take a while!")));
|
||||||
|
ps->SetIndeterminate();
|
||||||
|
if (bs.GetExactDuration()) {
|
||||||
|
LOG_D("bs") << "File cached and has exact samples.";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
properties = bs.GetAudioProperties();
|
||||||
|
float_samples = properties.IsFloat;
|
||||||
|
bytes_per_sample = properties.BytesPerSample;
|
||||||
|
sample_rate = properties.SampleRate;
|
||||||
|
channels = properties.Channels;
|
||||||
|
num_samples = properties.NumSamples;
|
||||||
|
decoded_samples = OPT_GET("Provider/Audio/BestSource/Aegisub Cache")->GetBool() ? 0 : num_samples;
|
||||||
|
}
|
||||||
|
catch (AudioException const& err) {
|
||||||
|
throw agi::AudioProviderError("Failed to create BestAudioSource");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taken from BestSource code and reversed
|
||||||
|
template<typename T>
|
||||||
|
static void PackChannels(const uint8_t *Src, void *Dst, size_t Length, size_t Channels) {
|
||||||
|
const T *S = reinterpret_cast<const T *>(Src);
|
||||||
|
T *D = reinterpret_cast<T *>(Dst);
|
||||||
|
for (size_t i = 0; i < Length; i++) {
|
||||||
|
for (size_t c = 0; c < Channels; c++)
|
||||||
|
D[c] = S[Length * c];
|
||||||
|
S += 1;
|
||||||
|
D += Channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSAudioProvider::FillBuffer(void *Buf, int64_t Start, int64_t Count) const {
|
||||||
|
// BS unpacked the channels, so until it gets a feature to disable that, let's just
|
||||||
|
// pack them in the same way they were unpacked
|
||||||
|
std::vector<uint8_t> unpacked_buf(channels * bytes_per_sample * Count);
|
||||||
|
std::vector<uint8_t *> bufs(channels);
|
||||||
|
for (int i = 0; i < channels; i++) {
|
||||||
|
bufs[i] = unpacked_buf.data() + i * bytes_per_sample * Count;
|
||||||
|
}
|
||||||
|
const_cast<BestAudioSource &>(bs).GetAudio(bufs.data(), Start, Count);
|
||||||
|
|
||||||
|
if (bytes_per_sample == 1)
|
||||||
|
PackChannels<uint8_t>(unpacked_buf.data(), Buf, Count, channels);
|
||||||
|
else if (bytes_per_sample == 2)
|
||||||
|
PackChannels<uint16_t>(unpacked_buf.data(), Buf, Count, channels);
|
||||||
|
else if (bytes_per_sample == 4)
|
||||||
|
PackChannels<uint32_t>(unpacked_buf.data(), Buf, Count, channels);
|
||||||
|
else if (bytes_per_sample == 8)
|
||||||
|
PackChannels<uint64_t>(unpacked_buf.data(), Buf, Count, channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<agi::AudioProvider> CreateBSAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *br) {
|
||||||
|
return agi::make_unique<BSAudioProvider>(file, br);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* WITH_BESTSOURCE */
|
||||||
|
|
|
@ -31,6 +31,7 @@ using namespace agi;
|
||||||
|
|
||||||
std::unique_ptr<AudioProvider> CreateAvisynthAudioProvider(fs::path const& filename, BackgroundRunner *);
|
std::unique_ptr<AudioProvider> CreateAvisynthAudioProvider(fs::path const& filename, BackgroundRunner *);
|
||||||
std::unique_ptr<AudioProvider> CreateFFmpegSourceAudioProvider(fs::path const& filename, BackgroundRunner *);
|
std::unique_ptr<AudioProvider> CreateFFmpegSourceAudioProvider(fs::path const& filename, BackgroundRunner *);
|
||||||
|
std::unique_ptr<AudioProvider> CreateBSAudioProvider(fs::path const& filename, BackgroundRunner *);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct factory {
|
struct factory {
|
||||||
|
@ -48,6 +49,9 @@ const factory providers[] = {
|
||||||
#ifdef WITH_AVISYNTH
|
#ifdef WITH_AVISYNTH
|
||||||
{"Avisynth", CreateAvisynthAudioProvider, false},
|
{"Avisynth", CreateAvisynthAudioProvider, false},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_BESTSOURCE
|
||||||
|
{"BestSource", CreateBSAudioProvider, false},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
49
src/bestsource_common.cpp
Normal file
49
src/bestsource_common.cpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (c) 2022, arch1t3cht <arch1t3cht@gmail.com>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
/// @file ffmpegsource_common.cpp
|
||||||
|
/// @brief Shared code for ffms video and audio providers
|
||||||
|
/// @ingroup video_input audio_input ffms
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifdef WITH_BESTSOURCE
|
||||||
|
#include "bestsource_common.h"
|
||||||
|
|
||||||
|
#include "options.h"
|
||||||
|
|
||||||
|
#include <libaegisub/fs.h>
|
||||||
|
#include <libaegisub/path.h>
|
||||||
|
|
||||||
|
#include <boost/crc.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
std::string GetBSCacheFile(agi::fs::path const& filename) {
|
||||||
|
// BS can store all its index data in a single file, but we make a separate index file
|
||||||
|
// for each video file to ensure that the old index is invalidated if the file is modified.
|
||||||
|
// While BS does check the filesize of the files, it doesn't check the modification time.
|
||||||
|
uintmax_t len = agi::fs::Size(filename);
|
||||||
|
boost::crc_32_type hash;
|
||||||
|
hash.process_bytes(filename.string().c_str(), filename.string().size());
|
||||||
|
|
||||||
|
auto result = config::path->Decode("?local/bsindex/" + filename.filename().string() + "_" + std::to_string(hash.checksum()) + "_" + std::to_string(len) + "_" + std::to_string(agi::fs::ModifiedTime(filename)) + ".json");
|
||||||
|
agi::fs::CreateDirectory(result.parent_path());
|
||||||
|
|
||||||
|
return result.string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // WITH_BESTSOURCE
|
28
src/bestsource_common.h
Normal file
28
src/bestsource_common.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (c) 2022, arch1t3cht <arch1t3cht@gmail.com>>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
/// @file ffmpegsource_common.h
|
||||||
|
/// @see ffmpegsource_common.cpp
|
||||||
|
/// @ingroup video_input audio_input ffms
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifdef WITH_BESTSOURCE
|
||||||
|
|
||||||
|
#include <libaegisub/fs_fwd.h>
|
||||||
|
|
||||||
|
std::string GetBSCacheFile(agi::fs::path const& filename);
|
||||||
|
|
||||||
|
#endif /* WITH_BESTSOURCE */
|
|
@ -332,6 +332,10 @@
|
||||||
},
|
},
|
||||||
"FFmpegSource" : {
|
"FFmpegSource" : {
|
||||||
"Decode Error Handling" : "ignore"
|
"Decode Error Handling" : "ignore"
|
||||||
|
},
|
||||||
|
"BestSource": {
|
||||||
|
"Max Cache Size" : 100,
|
||||||
|
"Aegisub Cache" : true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Avisynth" : {
|
"Avisynth" : {
|
||||||
|
|
|
@ -332,6 +332,10 @@
|
||||||
},
|
},
|
||||||
"FFmpegSource" : {
|
"FFmpegSource" : {
|
||||||
"Decode Error Handling" : "ignore"
|
"Decode Error Handling" : "ignore"
|
||||||
|
},
|
||||||
|
"BestSource": {
|
||||||
|
"Max Cache Size" : 100,
|
||||||
|
"Aegisub Cache" : true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Avisynth" : {
|
"Avisynth" : {
|
||||||
|
|
|
@ -224,10 +224,6 @@ if conf.has('WITH_CSRI')
|
||||||
aegisub_src += files('subtitles_provider_csri.cpp')
|
aegisub_src += files('subtitles_provider_csri.cpp')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if conf.has('WITH_BESTSOURCE')
|
|
||||||
aegisub_src += files('video_provider_bestsource.cpp')
|
|
||||||
endif
|
|
||||||
|
|
||||||
opt_src = [
|
opt_src = [
|
||||||
['ALSA', 'audio_player_alsa.cpp'],
|
['ALSA', 'audio_player_alsa.cpp'],
|
||||||
['PortAudio', 'audio_player_portaudio.cpp'],
|
['PortAudio', 'audio_player_portaudio.cpp'],
|
||||||
|
@ -240,6 +236,9 @@ opt_src = [
|
||||||
['FFMS2', ['audio_provider_ffmpegsource.cpp',
|
['FFMS2', ['audio_provider_ffmpegsource.cpp',
|
||||||
'video_provider_ffmpegsource.cpp',
|
'video_provider_ffmpegsource.cpp',
|
||||||
'ffmpegsource_common.cpp']],
|
'ffmpegsource_common.cpp']],
|
||||||
|
['BestSource', ['audio_provider_bestsource.cpp',
|
||||||
|
'video_provider_bestsource.cpp',
|
||||||
|
'bestsource_common.cpp']],
|
||||||
|
|
||||||
['Hunspell', 'spellchecker_hunspell.cpp'],
|
['Hunspell', 'spellchecker_hunspell.cpp'],
|
||||||
]
|
]
|
||||||
|
|
|
@ -403,6 +403,13 @@ void Advanced_Audio(wxTreebook *book, Preferences *parent) {
|
||||||
p->OptionAdd(ffms, _("Always index all audio tracks"), "Provider/FFmpegSource/Index All Tracks");
|
p->OptionAdd(ffms, _("Always index all audio tracks"), "Provider/FFmpegSource/Index All Tracks");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_BESTSOURCE
|
||||||
|
auto bs = p->PageSizer("BestSource");
|
||||||
|
p->OptionAdd(bs, _("Max BS cache size (MB)"), "Provider/Audio/BestSource/Max Cache Size");
|
||||||
|
p->OptionAdd(bs, _("Use Aegisub's Cache"), "Provider/Audio/BestSource/Aegisub Cache");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef WITH_PORTAUDIO
|
#ifdef WITH_PORTAUDIO
|
||||||
auto portaudio = p->PageSizer("Portaudio");
|
auto portaudio = p->PageSizer("Portaudio");
|
||||||
p->OptionChoice(portaudio, _("Portaudio device"), PortAudioPlayer::GetOutputDevices(), "Player/Audio/PortAudio/Device Name");
|
p->OptionChoice(portaudio, _("Portaudio device"), PortAudioPlayer::GetOutputDevices(), "Player/Audio/PortAudio/Device Name");
|
||||||
|
|
|
@ -32,7 +32,7 @@ extern "C" {
|
||||||
#include <libswscale/swscale.h>
|
#include <libswscale/swscale.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #include "utils.h" */
|
#include "bestsource_common.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "video_frame.h"
|
#include "video_frame.h"
|
||||||
|
@ -44,9 +44,6 @@ namespace agi { class BackgroundRunner; }
|
||||||
#include <libaegisub/background_runner.h>
|
#include <libaegisub/background_runner.h>
|
||||||
#include <libaegisub/log.h>
|
#include <libaegisub/log.h>
|
||||||
|
|
||||||
#include <boost/crc.hpp>
|
|
||||||
#include <boost/filesystem/path.hpp>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// @class BSVideoProvider
|
/// @class BSVideoProvider
|
||||||
|
@ -61,8 +58,6 @@ class BSVideoProvider final : public VideoProvider {
|
||||||
std::string colorspace;
|
std::string colorspace;
|
||||||
bool has_audio = false;
|
bool has_audio = false;
|
||||||
|
|
||||||
std::string GetCacheFile(agi::fs::path const& filename);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BSVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br);
|
BSVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br);
|
||||||
|
|
||||||
|
@ -108,7 +103,7 @@ std::string colormatrix_description(const AVFrame *frame) {
|
||||||
|
|
||||||
BSVideoProvider::BSVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br) try
|
BSVideoProvider::BSVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br) try
|
||||||
: bsopts()
|
: bsopts()
|
||||||
, bs(filename.string(), "", -1, false, OPT_GET("Provider/Video/BestSource/Threads")->GetInt(), GetCacheFile(filename), &bsopts)
|
, bs(filename.string(), "", -1, false, OPT_GET("Provider/Video/BestSource/Threads")->GetInt(), GetBSCacheFile(filename), &bsopts)
|
||||||
{
|
{
|
||||||
bs.SetMaxCacheSize(OPT_GET("Provider/Video/BestSource/Max Cache Size")->GetInt() << 20);
|
bs.SetMaxCacheSize(OPT_GET("Provider/Video/BestSource/Max Cache Size")->GetInt() << 20);
|
||||||
bs.SetSeekPreRoll(OPT_GET("Provider/Video/BestSource/Seek Preroll")->GetInt());
|
bs.SetSeekPreRoll(OPT_GET("Provider/Video/BestSource/Seek Preroll")->GetInt());
|
||||||
|
@ -171,20 +166,6 @@ catch (VideoException const& err) {
|
||||||
throw VideoOpenError("Failed to create BestVideoSource");
|
throw VideoOpenError("Failed to create BestVideoSource");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BSVideoProvider::GetCacheFile(agi::fs::path const& filename) {
|
|
||||||
// BS can store all its index data in a single file, but we make a separate index file
|
|
||||||
// for each video file to ensure that the old index is invalidated if the file is modified.
|
|
||||||
// While BS does check the filesize of the files, it doesn't check the modification time.
|
|
||||||
uintmax_t len = agi::fs::Size(filename);
|
|
||||||
boost::crc_32_type hash;
|
|
||||||
hash.process_bytes(filename.string().c_str(), filename.string().size());
|
|
||||||
|
|
||||||
auto result = config::path->Decode("?local/bsindex/" + filename.filename().string() + "_" + std::to_string(hash.checksum()) + "_" + std::to_string(len) + "_" + std::to_string(agi::fs::ModifiedTime(filename)) + ".json");
|
|
||||||
agi::fs::CreateDirectory(result.parent_path());
|
|
||||||
|
|
||||||
return result.string();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BSVideoProvider::GetFrame(int n, VideoFrame &out) {
|
void BSVideoProvider::GetFrame(int n, VideoFrame &out) {
|
||||||
std::unique_ptr<BestVideoFrame> bsframe(bs.GetFrame(n));
|
std::unique_ptr<BestVideoFrame> bsframe(bs.GetFrame(n));
|
||||||
if (bsframe == nullptr) {
|
if (bsframe == nullptr) {
|
||||||
|
|
Loading…
Reference in a new issue