bestsource: Bump to bestsource2
Update API usage, incorporate new features, and refactor a bit: - Use new progress callback to show progress bar - Use new frame info functions to get timecodes/keyframes instead of manually decoding - Use new tracklist API to show track selection dialogs - Expose option of whether to apply RFF - Show a warning when bestsource had to revert to linear decoding - Reuse the swscale context across frames and ensure the format stays constant - Add xxhash wrap - No longer mark the bestsource video provider as slow
This commit is contained in:
parent
4f8cb846cc
commit
900876cc7f
14 changed files with 230 additions and 112 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -30,7 +30,6 @@ subprojects/glib*
|
|||
subprojects/googletest-*
|
||||
subprojects/harfbuzz
|
||||
subprojects/icu
|
||||
subprojects/jansson
|
||||
subprojects/libass
|
||||
subprojects/libffi*
|
||||
subprojects/libpng-*
|
||||
|
@ -43,3 +42,4 @@ subprojects/zlib-*
|
|||
subprojects/dirent-*
|
||||
subprojects/hunspell-*
|
||||
subprojects/uchardet-*
|
||||
subprojects/xxhash
|
||||
|
|
|
@ -224,7 +224,7 @@ needs_ffmpeg = false
|
|||
|
||||
if get_option('bestsource').enabled()
|
||||
conf.set('WITH_BESTSOURCE', 1)
|
||||
bs = subproject('bestsource')
|
||||
bs = subproject('bestsource', default_options: ['link_static=' + (get_option('default_library') == 'static').to_string()])
|
||||
deps += bs.get_variable('bestsource_dep')
|
||||
dep_avail += 'BestSource'
|
||||
needs_ffmpeg = true
|
||||
|
|
|
@ -22,9 +22,10 @@
|
|||
#ifdef WITH_BESTSOURCE
|
||||
#include <libaegisub/audio/provider.h>
|
||||
|
||||
#include "bestsource_common.h"
|
||||
|
||||
#include "audiosource.h"
|
||||
|
||||
#include "bestsource_common.h"
|
||||
#include "compat.h"
|
||||
#include "options.h"
|
||||
|
||||
|
@ -38,7 +39,7 @@
|
|||
namespace {
|
||||
class BSAudioProvider final : public agi::AudioProvider {
|
||||
std::map<std::string, std::string> bsopts;
|
||||
BestAudioSource bs;
|
||||
std::unique_ptr<BestAudioSource> bs;
|
||||
AudioProperties properties;
|
||||
|
||||
void FillBuffer(void *Buf, int64_t Start, int64_t Count) const override;
|
||||
|
@ -52,32 +53,49 @@ public:
|
|||
/// @param filename The filename to open
|
||||
BSAudioProvider::BSAudioProvider(agi::fs::path const& filename, agi::BackgroundRunner *br) try
|
||||
: bsopts()
|
||||
, bs(filename.string(), -1, -1, 0, GetBSCacheFile(filename), &bsopts)
|
||||
{
|
||||
bs.SetMaxCacheSize(OPT_GET("Provider/Audio/BestSource/Max Cache Size")->GetInt() << 20);
|
||||
provider_bs::CleanBSCache();
|
||||
auto track = provider_bs::SelectTrack(filename, true).first;
|
||||
|
||||
if (track == provider_bs::TrackSelection::NoTracks)
|
||||
throw agi::AudioDataNotFound("no audio tracks found");
|
||||
else if (track == provider_bs::TrackSelection::None)
|
||||
throw agi::UserCancelException("audio loading cancelled by user");
|
||||
|
||||
bool cancelled = false;
|
||||
br->Run([&](agi::ProgressSink *ps) {
|
||||
ps->SetTitle(from_wx(_("Indexing")));
|
||||
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.";
|
||||
ps->SetMessage(from_wx(_("Indexing file... This will take a while!")));
|
||||
try {
|
||||
bs = agi::make_unique<BestAudioSource>(filename.string(), static_cast<int>(track), -1, false, 0, provider_bs::GetCacheFile(filename), &bsopts, 0, [=](int Track, int64_t Current, int64_t Total) {
|
||||
ps->SetProgress(Current, Total);
|
||||
return !ps->IsCancelled();
|
||||
});
|
||||
} catch (BestSourceException const& err) {
|
||||
if (std::string(err.what()) == "Indexing canceled by user")
|
||||
cancelled = true;
|
||||
else
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
BSCleanCache();
|
||||
properties = bs.GetAudioProperties();
|
||||
float_samples = properties.IsFloat;
|
||||
bytes_per_sample = properties.BytesPerSample;
|
||||
if (cancelled)
|
||||
throw agi::UserCancelException("audio loading cancelled by user");
|
||||
|
||||
bs->SetMaxCacheSize(OPT_GET("Provider/Audio/BestSource/Max Cache Size")->GetInt() << 20);
|
||||
properties = bs->GetAudioProperties();
|
||||
float_samples = properties.AF.Float;
|
||||
bytes_per_sample = properties.AF.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) {
|
||||
catch (BestSourceException const& err) {
|
||||
throw agi::AudioProviderError("Failed to create BestAudioSource");
|
||||
}
|
||||
|
||||
void BSAudioProvider::FillBuffer(void *Buf, int64_t Start, int64_t Count) const {
|
||||
const_cast<BestAudioSource &>(bs).GetPackedAudio(reinterpret_cast<uint8_t *>(Buf), Start, Count);
|
||||
bs->GetPackedAudio(reinterpret_cast<uint8_t *>(Buf), Start, Count);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,13 @@
|
|||
|
||||
#ifdef WITH_BESTSOURCE
|
||||
#include "bestsource_common.h"
|
||||
#include "tracklist.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/avutil.h>
|
||||
}
|
||||
|
||||
#include "format.h"
|
||||
#include "options.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -31,26 +37,70 @@
|
|||
#include <boost/crc.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace provider_bs {
|
||||
|
||||
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);
|
||||
std::pair<TrackSelection, bool> SelectTrack(agi::fs::path const& filename, bool audio) {
|
||||
std::map<std::string, std::string> opts;
|
||||
BestTrackList tracklist(filename.string(), &opts);
|
||||
|
||||
int n = tracklist.GetNumTracks();
|
||||
AVMediaType type = audio ? AVMEDIA_TYPE_AUDIO : AVMEDIA_TYPE_VIDEO;
|
||||
|
||||
std::vector<int> TrackNumbers;
|
||||
wxArrayString Choices;
|
||||
bool has_audio = false;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
BestTrackList::TrackInfo info = tracklist.GetTrackInfo(i);
|
||||
has_audio = has_audio || (info.MediaType == AVMEDIA_TYPE_AUDIO);
|
||||
|
||||
if (info.MediaType == type) {
|
||||
TrackNumbers.push_back(i);
|
||||
Choices.Add(agi::wxformat(_("Track %02d: %s"), i, info.CodecString));
|
||||
}
|
||||
}
|
||||
|
||||
TrackSelection result;
|
||||
|
||||
if (TrackNumbers.empty()) {
|
||||
result = TrackSelection::NoTracks;
|
||||
} else if (TrackNumbers.size() == 1) {
|
||||
result = static_cast<TrackSelection>(TrackNumbers[0]);
|
||||
} else {
|
||||
int Choice = wxGetSingleChoiceIndex(
|
||||
audio ? _("Multiple video tracks detected, please choose the one you wish to load:") : _("Multiple audio tracks detected, please choose the one you wish to load:"),
|
||||
audio ? _("Choose video track") : _("Choose audio track"),
|
||||
Choices);
|
||||
|
||||
if (Choice >= 0)
|
||||
result = static_cast<TrackSelection>(TrackNumbers[Choice]) ;
|
||||
else
|
||||
result = TrackSelection::None;
|
||||
}
|
||||
|
||||
return std::make_pair(result, has_audio);
|
||||
}
|
||||
|
||||
std::string GetCacheFile(agi::fs::path const& 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");
|
||||
auto result = config::path->Decode("?local/bsindex/" + filename.filename().string() + "_" + std::to_string(hash.checksum()) + "_" + std::to_string(agi::fs::ModifiedTime(filename)));
|
||||
agi::fs::CreateDirectory(result.parent_path());
|
||||
|
||||
return result.string();
|
||||
}
|
||||
|
||||
void BSCleanCache() {
|
||||
void CleanBSCache() {
|
||||
CleanCache(config::path->Decode("?local/bsindex/"),
|
||||
"*.json",
|
||||
"*.bsindex",
|
||||
OPT_GET("Provider/BestSource/Cache/Size")->GetInt(),
|
||||
OPT_GET("Provider/BestSource/Cache/Files")->GetInt());
|
||||
|
||||
// Delete old cache files: TODO remove this after a while
|
||||
CleanCache(config::path->Decode("?local/bsindex/"),
|
||||
"*.json", 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WITH_BESTSOURCE
|
||||
|
|
|
@ -21,9 +21,27 @@
|
|||
|
||||
#ifdef WITH_BESTSOURCE
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
namespace std { class string_view; }
|
||||
|
||||
std::string GetBSCacheFile(agi::fs::path const& filename);
|
||||
void BSCleanCache();
|
||||
#include <bsshared.h>
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
#include <libaegisub/background_runner.h>
|
||||
|
||||
namespace provider_bs {
|
||||
|
||||
// X11 memes
|
||||
#undef None
|
||||
|
||||
enum class TrackSelection : int {
|
||||
None = -1,
|
||||
NoTracks = -2,
|
||||
};
|
||||
|
||||
std::pair<TrackSelection, bool> SelectTrack(agi::fs::path const& filename, bool audio);
|
||||
std::string GetCacheFile(agi::fs::path const& filename);
|
||||
void CleanBSCache();
|
||||
|
||||
}
|
||||
|
||||
#endif /* WITH_BESTSOURCE */
|
||||
|
|
|
@ -367,6 +367,7 @@
|
|||
"BestSource" : {
|
||||
"Max Cache Size" : 1024,
|
||||
"Threads" : 0,
|
||||
"Apply RFF": true,
|
||||
"Seek Preroll" : 12
|
||||
}
|
||||
}
|
||||
|
|
|
@ -367,6 +367,7 @@
|
|||
"BestSource" : {
|
||||
"Max Cache Size" : 1024,
|
||||
"Threads" : 0,
|
||||
"Apply RFF": true,
|
||||
"Seek Preroll" : 12
|
||||
}
|
||||
}
|
||||
|
|
|
@ -464,6 +464,7 @@ void Advanced_Video(wxTreebook *book, Preferences *parent) {
|
|||
p->OptionAdd(bs, _("Max cache size (MB)"), "Provider/Video/BestSource/Max Cache Size");
|
||||
p->OptionAdd(bs, _("Decoder Threads (0 to autodetect)"), "Provider/Video/BestSource/Threads");
|
||||
p->OptionAdd(bs, _("Seek preroll (Frames)"), "Provider/Video/BestSource/Seek Preroll");
|
||||
p->OptionAdd(bs, _("Apply RFF"), "Provider/Video/BestSource/Apply RFF");
|
||||
#endif
|
||||
|
||||
p->SetSizerAndFit(p->sizer);
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
#ifdef WITH_BESTSOURCE
|
||||
#include "include/aegisub/video_provider.h"
|
||||
|
||||
namespace std { class string_view; }
|
||||
|
||||
#include "bestsource_common.h"
|
||||
|
||||
#include "videosource.h"
|
||||
#include "audiosource.h"
|
||||
#include "BSRational.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/frame.h>
|
||||
|
@ -32,7 +34,6 @@ extern "C" {
|
|||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include "bestsource_common.h"
|
||||
#include "options.h"
|
||||
#include "compat.h"
|
||||
#include "video_frame.h"
|
||||
|
@ -40,10 +41,12 @@ namespace agi { class BackgroundRunner; }
|
|||
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/path.h>
|
||||
#include <libaegisub/dispatch.h>
|
||||
#include <libaegisub/make_unique.h>
|
||||
#include <libaegisub/background_runner.h>
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/format.h>
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -51,14 +54,21 @@ namespace {
|
|||
/// @brief Implements video loading through BestSource.
|
||||
class BSVideoProvider final : public VideoProvider {
|
||||
std::map<std::string, std::string> bsopts;
|
||||
BestVideoSource bs;
|
||||
bool apply_rff;
|
||||
|
||||
std::unique_ptr<BestVideoSource> bs;
|
||||
VideoProperties properties;
|
||||
|
||||
std::vector<int> Keyframes;
|
||||
agi::vfr::Framerate Timecodes;
|
||||
AVPixelFormat pixfmt;
|
||||
std::string colorspace;
|
||||
bool has_audio = false;
|
||||
|
||||
bool is_linear = false;
|
||||
|
||||
agi::scoped_holder<SwsContext *> sws_context;
|
||||
|
||||
public:
|
||||
BSVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br);
|
||||
|
||||
|
@ -102,27 +112,42 @@ 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(), "", 0, -1, false, OPT_GET("Provider/Video/BestSource/Threads")->GetInt(), GetBSCacheFile(filename), &bsopts)
|
||||
: apply_rff(OPT_GET("Provider/Video/BestSource/Apply RFF"))
|
||||
, sws_context(nullptr, sws_freeContext)
|
||||
{
|
||||
bs.SetMaxCacheSize(OPT_GET("Provider/Video/BestSource/Max Cache Size")->GetInt() << 20);
|
||||
bs.SetSeekPreRoll(OPT_GET("Provider/Video/BestSource/Seek Preroll")->GetInt());
|
||||
try {
|
||||
BestAudioSource dummysource(filename.string(), -1, 0, 0, "");
|
||||
has_audio = true;
|
||||
} catch (AudioException const& err) {
|
||||
has_audio = false;
|
||||
}
|
||||
provider_bs::CleanBSCache();
|
||||
|
||||
auto track_info = provider_bs::SelectTrack(filename, false);
|
||||
has_audio = track_info.second;
|
||||
|
||||
if (track_info.first == provider_bs::TrackSelection::NoTracks)
|
||||
throw VideoNotSupported("no video tracks found");
|
||||
else if (track_info.first == provider_bs::TrackSelection::None)
|
||||
throw agi::UserCancelException("video loading cancelled by user");
|
||||
|
||||
bool cancelled = false;
|
||||
br->Run([&](agi::ProgressSink *ps) {
|
||||
ps->SetTitle(from_wx(_("Indexing")));
|
||||
ps->SetMessage(from_wx(_("Creating cache... This can take a while!")));
|
||||
ps->SetIndeterminate();
|
||||
if (bs.GetExactDuration()) {
|
||||
LOG_D("provider/video/bestsource") << "File cached and has exact samples.";
|
||||
ps->SetMessage(from_wx(_("Decoding the full track to ensure perfect frame accuracy. This will take a while!")));
|
||||
try {
|
||||
bs = agi::make_unique<BestVideoSource>(filename.string(), "", 0, static_cast<int>(track_info.first), false, OPT_GET("Provider/Video/BestSource/Threads")->GetInt(), provider_bs::GetCacheFile(filename), &bsopts, [=](int Track, int64_t Current, int64_t Total) {
|
||||
ps->SetProgress(Current, Total);
|
||||
return !ps->IsCancelled();
|
||||
});
|
||||
} catch (BestSourceException const& err) {
|
||||
if (std::string(err.what()) == "Indexing canceled by user")
|
||||
cancelled = true;
|
||||
else
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
properties = bs.GetVideoProperties();
|
||||
if (cancelled)
|
||||
throw agi::UserCancelException("video loading cancelled by user");
|
||||
|
||||
bs->SetMaxCacheSize(OPT_GET("Provider/Video/BestSource/Max Cache Size")->GetInt() << 20);
|
||||
bs->SetSeekPreRoll(OPT_GET("Provider/Video/BestSource/Seek Preroll")->GetInt());
|
||||
|
||||
properties = bs->GetVideoProperties();
|
||||
|
||||
br->Run([&](agi::ProgressSink *ps) {
|
||||
ps->SetTitle(from_wx(_("Scanning")));
|
||||
|
@ -130,23 +155,18 @@ BSVideoProvider::BSVideoProvider(agi::fs::path const& filename, std::string cons
|
|||
|
||||
std::vector<int> TimecodesVector;
|
||||
for (int n = 0; n < properties.NumFrames; n++) {
|
||||
if (ps->IsCancelled()) {
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<BestVideoFrame> frame(bs.GetFrame(n));
|
||||
if (frame == nullptr) {
|
||||
throw VideoOpenError("Couldn't read frame!");
|
||||
}
|
||||
#if (LIBAVUTIL_VERSION_MAJOR == 58 && LIBAVUTIL_VERSION_MINOR >= 7) || LIBAVUTIL_VERSION_MAJOR >= 59
|
||||
if (frame->GetAVFrame()->flags & AV_FRAME_FLAG_KEY) {
|
||||
#else
|
||||
if (frame->GetAVFrame()->key_frame) {
|
||||
#endif
|
||||
const BestVideoSource::FrameInfo &info = bs->GetFrameInfo(n);
|
||||
if (info.KeyFrame) {
|
||||
Keyframes.push_back(n);
|
||||
}
|
||||
|
||||
TimecodesVector.push_back(1000 * frame->Pts * properties.TimeBase.Num / properties.TimeBase.Den);
|
||||
ps->SetProgress(n, properties.NumFrames);
|
||||
TimecodesVector.push_back(1000 * info.PTS * properties.TimeBase.Num / properties.TimeBase.Den);
|
||||
|
||||
if (n % 16 == 0) {
|
||||
if (ps->IsCancelled())
|
||||
return;
|
||||
ps->SetProgress(n, properties.NumFrames);
|
||||
}
|
||||
}
|
||||
|
||||
if (TimecodesVector.size() < 2 || TimecodesVector.front() == TimecodesVector.back()) {
|
||||
|
@ -156,51 +176,63 @@ BSVideoProvider::BSVideoProvider(agi::fs::path const& filename, std::string cons
|
|||
}
|
||||
});
|
||||
|
||||
BSCleanCache();
|
||||
// Decode the first frame to get the color space and pixel format
|
||||
std::unique_ptr<BestVideoFrame> frame(bs->GetFrame(0));
|
||||
auto avframe = frame->GetAVFrame();
|
||||
colorspace = colormatrix_description(avframe);
|
||||
pixfmt = (AVPixelFormat) avframe->format;
|
||||
|
||||
sws_context = sws_getContext(
|
||||
properties.Width, properties.Height, pixfmt,
|
||||
properties.Width, properties.Height, AV_PIX_FMT_BGR0,
|
||||
SWS_BICUBIC, nullptr, nullptr, nullptr);
|
||||
|
||||
if (sws_context == nullptr) {
|
||||
throw VideoDecodeError("Cannot convert frame to RGB!");
|
||||
}
|
||||
|
||||
// Decode the first frame to get the color space
|
||||
std::unique_ptr<BestVideoFrame> frame(bs.GetFrame(0));
|
||||
colorspace = colormatrix_description(frame->GetAVFrame());
|
||||
}
|
||||
catch (VideoException const& err) {
|
||||
catch (BestSourceException const& err) {
|
||||
throw VideoOpenError(agi::format("Failed to create BestVideoSource: %s", + err.what()));
|
||||
}
|
||||
|
||||
void BSVideoProvider::GetFrame(int n, VideoFrame &out) {
|
||||
std::unique_ptr<BestVideoFrame> bsframe(bs.GetFrame(n));
|
||||
std::unique_ptr<BestVideoFrame> bsframe(apply_rff ? bs->GetFrameWithRFF(n) : bs->GetFrame(n));
|
||||
if (bsframe == nullptr) {
|
||||
throw VideoDecodeError("Couldn't read frame!");
|
||||
}
|
||||
const AVFrame *frame = bsframe->GetAVFrame();
|
||||
|
||||
SwsContext *context = sws_getContext(
|
||||
frame->width, frame->height, (AVPixelFormat) frame->format, // TODO figure out aegi's color space forcing.
|
||||
frame->width, frame->height, AV_PIX_FMT_BGR0,
|
||||
SWS_BICUBIC, nullptr, nullptr, nullptr);
|
||||
if (!is_linear && bs->GetLinearDecodingState()) {
|
||||
agi::dispatch::Main().Async([] {
|
||||
wxMessageBox(_("BestSource had to fall back to linear decoding. Seeking through the video will be very slow now. You may want to try a different video provider, but note that those are not guaranteed to be frame-exact."), _("Warning"), wxOK | wxICON_WARNING | wxCENTER);
|
||||
});
|
||||
|
||||
if (context == nullptr) {
|
||||
throw VideoDecodeError("Couldn't convert frame!");
|
||||
is_linear = true;
|
||||
}
|
||||
|
||||
const AVFrame *frame = bsframe->GetAVFrame();
|
||||
|
||||
int range = frame->color_range == AVCOL_RANGE_JPEG;
|
||||
const int *coefficients = sws_getCoefficients(frame->colorspace == AVCOL_SPC_UNSPECIFIED ? AVCOL_SPC_BT709 : frame->colorspace);
|
||||
|
||||
sws_setColorspaceDetails(context,
|
||||
coefficients, range,
|
||||
coefficients, range,
|
||||
0, 1 << 16, 1 << 16);
|
||||
if (frame->format != pixfmt || frame->width != properties.Width || frame->height != properties.Height)
|
||||
throw VideoDecodeError("Video has variable format!");
|
||||
|
||||
// TODO apply color space forcing.
|
||||
sws_setColorspaceDetails(sws_context,
|
||||
coefficients, range,
|
||||
coefficients, range,
|
||||
0, 1 << 16, 1 << 16);
|
||||
|
||||
out.data.resize(frame->width * frame->height * 4);
|
||||
uint8_t *data[1] = {&out.data[0]};
|
||||
int stride[1] = {frame->width * 4};
|
||||
sws_scale(context, frame->data, frame->linesize, 0, frame->height, data, stride);
|
||||
sws_scale(sws_context, frame->data, frame->linesize, 0, frame->height, data, stride);
|
||||
|
||||
out.width = frame->width;
|
||||
out.height = frame->height;
|
||||
out.pitch = stride[0];
|
||||
out.flipped = false; // TODO figure out flipped
|
||||
|
||||
sws_freeContext(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace {
|
|||
{"Avisynth", CreateAvisynthVideoProvider, false},
|
||||
#endif
|
||||
#ifdef WITH_BESTSOURCE
|
||||
{"BestSource (SLOW)", CreateBSVideoProvider, false},
|
||||
{"BestSource", CreateBSVideoProvider, false},
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[wrap-git]
|
||||
url = https://github.com/vapoursynth/bestsource
|
||||
revision = R1
|
||||
revision = 9d7e218588867bf2b1334e5382b0f4d1b6a45aa1
|
||||
clone-recursive = true
|
||||
diff_files = bestsource/0001.patch
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[wrap-git]
|
||||
directory = jansson
|
||||
url = https://github.com/akheron/jansson.git
|
||||
revision = v2.14
|
|
@ -1,9 +1,11 @@
|
|||
diff --git a/meson.build b/meson.build
|
||||
index 38de461..d56af62 100644
|
||||
index f7bdbda..3351e53 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -2,10 +2,6 @@ project('BestSource', 'cpp',
|
||||
default_options: ['buildtype=release', 'b_lto=true', 'b_ndebug=if-release', 'cpp_std=c++14'],
|
||||
@@ -1,21 +1,15 @@
|
||||
project('BestSource', 'cpp',
|
||||
- default_options: ['buildtype=release', 'b_lto=true', 'b_ndebug=if-release', 'cpp_std=c++17'],
|
||||
+ default_options: ['buildtype=release', 'b_ndebug=if-release', 'cpp_std=c++17'],
|
||||
license: 'MIT',
|
||||
meson_version: '>=0.53.0',
|
||||
- version: '.'.join([
|
||||
|
@ -13,15 +15,17 @@ index 38de461..d56af62 100644
|
|||
)
|
||||
|
||||
link_static = get_option('link_static')
|
||||
@@ -15,7 +11,6 @@ sources = [
|
||||
'src/BSRational.cpp',
|
||||
'src/BSShared.cpp',
|
||||
'src/SrcAttribCache.cpp',
|
||||
|
||||
sources = [
|
||||
'src/audiosource.cpp',
|
||||
- 'src/avisynth.cpp',
|
||||
'src/bsshared.cpp',
|
||||
'src/tracklist.cpp',
|
||||
- 'src/vapoursynth.cpp',
|
||||
'src/videosource.cpp'
|
||||
]
|
||||
|
||||
@@ -46,17 +41,23 @@ if host_machine.cpu_family().startswith('x86')
|
||||
@@ -46,10 +40,7 @@ if host_machine.cpu_family().startswith('x86')
|
||||
)
|
||||
endif
|
||||
|
||||
|
@ -29,32 +33,16 @@ index 38de461..d56af62 100644
|
|||
-
|
||||
deps = [
|
||||
- vapoursynth_dep,
|
||||
- dependency('jansson', version: '>=2.12', static: link_static),
|
||||
dependency('libavcodec', version: '>=60.31.0', static: link_static),
|
||||
dependency('libavformat', version: '>=60.16.0', static: link_static),
|
||||
dependency('libavutil', version: '>=58.29.0', static: link_static),
|
||||
dependency('libswscale', version: '>=7.5.0', static: link_static)
|
||||
]
|
||||
|
||||
+jansson_dep = dependency('jansson', version: '>= 2.12', required: false)
|
||||
+
|
||||
+if jansson_dep.found()
|
||||
+ deps += jansson_dep
|
||||
+else
|
||||
+ cmake = import('cmake')
|
||||
+ jansson = cmake.subproject('jansson')
|
||||
+ deps += jansson.dependency('jansson')
|
||||
+endif
|
||||
+
|
||||
is_gnu_linker = meson.get_compiler('cpp').get_linker_id() in ['ld.bfd', 'ld.gold', 'ld.mold']
|
||||
link_args = []
|
||||
|
||||
@@ -66,11 +67,11 @@ elif is_gnu_linker
|
||||
@@ -65,12 +56,12 @@ elif is_gnu_linker
|
||||
link_args += ['-Wl,-Bsymbolic']
|
||||
endif
|
||||
|
||||
-shared_module('bestsource', sources,
|
||||
+bs_lib = static_library('bestsource', sources,
|
||||
cpp_args: ['-D_FILE_OFFSET_BITS=64'],
|
||||
dependencies: deps,
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
- install: true,
|
||||
|
|
13
subprojects/xxhash.wrap
Normal file
13
subprojects/xxhash.wrap
Normal file
|
@ -0,0 +1,13 @@
|
|||
[wrap-file]
|
||||
directory = xxHash-0.8.2
|
||||
source_url = https://github.com/Cyan4973/xxHash/archive/v0.8.2.tar.gz
|
||||
source_filename = xxHash-0.8.2.tar.gz
|
||||
source_hash = baee0c6afd4f03165de7a4e67988d16f0f2b257b51d0e3cb91909302a26a79c4
|
||||
patch_filename = xxhash_0.8.2-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/xxhash_0.8.2-1/get_patch
|
||||
patch_hash = e721ef7a4c4ee0ade8b8440f6f7cb9f935b68e825249d74cb1c2503c53e68d25
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/xxhash_0.8.2-1/xxHash-0.8.2.tar.gz
|
||||
wrapdb_version = 0.8.2-1
|
||||
|
||||
[provide]
|
||||
libxxhash = xxhash_dep
|
Loading…
Reference in a new issue