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)
|
||||
bs = subproject('bestsource')
|
||||
deps += bs.get_variable('bestsource_dep')
|
||||
dep_avail += 'BestSource'
|
||||
needs_ffmpeg = true
|
||||
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> CreateFFmpegSourceAudioProvider(fs::path const& filename, BackgroundRunner *);
|
||||
std::unique_ptr<AudioProvider> CreateBSAudioProvider(fs::path const& filename, BackgroundRunner *);
|
||||
|
||||
namespace {
|
||||
struct factory {
|
||||
|
@ -48,6 +49,9 @@ const factory providers[] = {
|
|||
#ifdef WITH_AVISYNTH
|
||||
{"Avisynth", CreateAvisynthAudioProvider, false},
|
||||
#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" : {
|
||||
"Decode Error Handling" : "ignore"
|
||||
},
|
||||
"BestSource": {
|
||||
"Max Cache Size" : 100,
|
||||
"Aegisub Cache" : true
|
||||
}
|
||||
},
|
||||
"Avisynth" : {
|
||||
|
|
|
@ -332,6 +332,10 @@
|
|||
},
|
||||
"FFmpegSource" : {
|
||||
"Decode Error Handling" : "ignore"
|
||||
},
|
||||
"BestSource": {
|
||||
"Max Cache Size" : 100,
|
||||
"Aegisub Cache" : true
|
||||
}
|
||||
},
|
||||
"Avisynth" : {
|
||||
|
|
|
@ -224,10 +224,6 @@ if conf.has('WITH_CSRI')
|
|||
aegisub_src += files('subtitles_provider_csri.cpp')
|
||||
endif
|
||||
|
||||
if conf.has('WITH_BESTSOURCE')
|
||||
aegisub_src += files('video_provider_bestsource.cpp')
|
||||
endif
|
||||
|
||||
opt_src = [
|
||||
['ALSA', 'audio_player_alsa.cpp'],
|
||||
['PortAudio', 'audio_player_portaudio.cpp'],
|
||||
|
@ -240,6 +236,9 @@ opt_src = [
|
|||
['FFMS2', ['audio_provider_ffmpegsource.cpp',
|
||||
'video_provider_ffmpegsource.cpp',
|
||||
'ffmpegsource_common.cpp']],
|
||||
['BestSource', ['audio_provider_bestsource.cpp',
|
||||
'video_provider_bestsource.cpp',
|
||||
'bestsource_common.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");
|
||||
#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
|
||||
auto portaudio = p->PageSizer("Portaudio");
|
||||
p->OptionChoice(portaudio, _("Portaudio device"), PortAudioPlayer::GetOutputDevices(), "Player/Audio/PortAudio/Device Name");
|
||||
|
|
|
@ -32,7 +32,7 @@ extern "C" {
|
|||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
/* #include "utils.h" */
|
||||
#include "bestsource_common.h"
|
||||
#include "options.h"
|
||||
#include "compat.h"
|
||||
#include "video_frame.h"
|
||||
|
@ -44,9 +44,6 @@ namespace agi { class BackgroundRunner; }
|
|||
#include <libaegisub/background_runner.h>
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include <boost/crc.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
/// @class BSVideoProvider
|
||||
|
@ -61,8 +58,6 @@ class BSVideoProvider final : public VideoProvider {
|
|||
std::string colorspace;
|
||||
bool has_audio = false;
|
||||
|
||||
std::string GetCacheFile(agi::fs::path const& filename);
|
||||
|
||||
public:
|
||||
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
|
||||
: 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.SetSeekPreRoll(OPT_GET("Provider/Video/BestSource/Seek Preroll")->GetInt());
|
||||
|
@ -171,20 +166,6 @@ catch (VideoException const& err) {
|
|||
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) {
|
||||
std::unique_ptr<BestVideoFrame> bsframe(bs.GetFrame(n));
|
||||
if (bsframe == nullptr) {
|
||||
|
|
Loading…
Reference in a new issue