nodes: revise means of limiting array elements
This commit is contained in:
+31
-2
@@ -212,9 +212,38 @@ public:
|
||||
return input_ids_;
|
||||
}
|
||||
|
||||
virtual bool IsInputActiveAtTime(const QString &input, int element, const TimeRange &r) const
|
||||
class ActiveElements
|
||||
{
|
||||
return true;
|
||||
public:
|
||||
enum Mode {
|
||||
kAllElements,
|
||||
kSpecified,
|
||||
kNoElements
|
||||
};
|
||||
|
||||
ActiveElements(Mode m = kAllElements)
|
||||
{
|
||||
mode_ = m;
|
||||
}
|
||||
|
||||
Mode mode() const { return mode_; }
|
||||
std::list<int> elements() const { return elements_; }
|
||||
|
||||
void add(int e)
|
||||
{
|
||||
elements_.push_back(e);
|
||||
mode_ = kSpecified;
|
||||
}
|
||||
|
||||
private:
|
||||
Mode mode_;
|
||||
std::list<int> elements_;
|
||||
|
||||
};
|
||||
|
||||
virtual ActiveElements GetActiveElementsAtTime(const QString &input, const TimeRange &r) const
|
||||
{
|
||||
return ActiveElements::kAllElements;
|
||||
}
|
||||
|
||||
bool HasInputWithID(const QString& id) const
|
||||
|
||||
+169
-53
@@ -24,8 +24,10 @@
|
||||
#include <QDebug>
|
||||
#include <QFontMetrics>
|
||||
|
||||
#include "audio/audioprocessor.h"
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/graph.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -95,6 +97,51 @@ QString Track::Description() const
|
||||
"a Sequence.");
|
||||
}
|
||||
|
||||
Node::ActiveElements Track::GetActiveElementsAtTime(const QString &input, const TimeRange &r) const
|
||||
{
|
||||
if (input == kBlockInput) {
|
||||
if (IsMuted() || blocks_.empty() || r.in() >= track_length() || r.out() <= 0) {
|
||||
return ActiveElements::kNoElements;
|
||||
} else {
|
||||
int start = GetBlockIndexAtTime(r.in());
|
||||
int end = GetBlockIndexAtTime(r.out());
|
||||
|
||||
if (start == -1) {
|
||||
start = 0;
|
||||
}
|
||||
if (end == -1) {
|
||||
end = blocks_.size()-1;
|
||||
}
|
||||
|
||||
ActiveElements a;
|
||||
for (int i=start; i<=end; i++) {
|
||||
Block *b = blocks_.at(i);
|
||||
if (b->is_enabled()) {
|
||||
a.add(GetArrayIndexFromCacheIndex(i));
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
} else {
|
||||
return super::GetActiveElementsAtTime(input, r);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
if (this->type() == Track::kVideo) {
|
||||
// Just pass straight through
|
||||
NodeValueArray a = value[kBlockInput].toArray();
|
||||
if (!a.empty()) {
|
||||
table->Push(a.begin()->second);
|
||||
}
|
||||
} else if (this->type() == Track::kAudio) {
|
||||
// Audio
|
||||
ProcessAudioTrack(table, globals.time());
|
||||
}
|
||||
}
|
||||
|
||||
TimeRange Track::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const
|
||||
{
|
||||
if (input == kBlockInput && element >= 0) {
|
||||
@@ -352,58 +399,6 @@ Block *Track::NearestBlockAfter(const rational &time) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Block *Track::BlockAtTime(const rational &time) const
|
||||
{
|
||||
if (IsMuted() || time > track_length() || blocks_.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Use binary search to find block at time
|
||||
Block* using_block = nullptr;
|
||||
|
||||
int low = 0;
|
||||
int high = blocks_.size() - 1;
|
||||
while (low <= high) {
|
||||
int mid = low + (high - low) / 2;
|
||||
|
||||
Block* block = blocks_.at(mid);
|
||||
if (block->in() <= time && block->out() > time) {
|
||||
using_block = block;
|
||||
break;
|
||||
} else if (block->out() <= time) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (using_block && !using_block->is_enabled()) {
|
||||
using_block = nullptr;
|
||||
}
|
||||
|
||||
return using_block;
|
||||
}
|
||||
|
||||
QVector<Block *> Track::BlocksAtTimeRange(const TimeRange &range) const
|
||||
{
|
||||
QVector<Block*> list;
|
||||
|
||||
if (IsMuted()) {
|
||||
return list;
|
||||
}
|
||||
|
||||
foreach (Block* block, blocks_) {
|
||||
if (block
|
||||
&& block->is_enabled()
|
||||
&& block->out() > range.in()
|
||||
&& block->in() < range.out()) {
|
||||
list.append(block);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void Track::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
TimeRange limited;
|
||||
@@ -579,6 +574,127 @@ int Track::GetCacheIndexFromArrayIndex(int index) const
|
||||
return block_array_indexes_.indexOf(index);
|
||||
}
|
||||
|
||||
int Track::GetBlockIndexAtTime(const rational &time) const
|
||||
{
|
||||
if (time < 0 || time >= track_length()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Use binary search to find block at time
|
||||
int low = 0;
|
||||
int high = blocks_.size() - 1;
|
||||
while (low <= high) {
|
||||
int mid = low + (high - low) / 2;
|
||||
|
||||
Block* block = blocks_.at(mid);
|
||||
if (block->in() <= time && block->out() > time) {
|
||||
return mid;
|
||||
} else if (block->out() <= time) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Track::ProcessAudioTrack(NodeValueTable *table, const TimeRange &range) const
|
||||
{
|
||||
/*
|
||||
// All these blocks will need to output to a buffer so we create one here
|
||||
SampleBuffer block_range_buffer(audio_params, range.length());
|
||||
block_range_buffer.silence();
|
||||
|
||||
NodeValueTable merged_table;
|
||||
|
||||
// Loop through active blocks retrieving their audio
|
||||
foreach (Block* b, active_blocks) {
|
||||
if (dynamic_cast<ClipBlock*>(b) || dynamic_cast<TransitionBlock*>(b)) {
|
||||
TimeRange range_for_block(qMax(b->in(), range.in()),
|
||||
qMin(b->out(), range.out()));
|
||||
|
||||
qint64 destination_offset = audio_params.time_to_samples(range_for_block.in() - range.in());
|
||||
qint64 max_dest_sz = audio_params.time_to_samples(range_for_block.length());
|
||||
|
||||
// Destination buffer
|
||||
NodeValueTable table = GenerateTable(b, Track::TransformRangeForBlock(b, range_for_block));
|
||||
SampleBuffer samples_from_this_block = table.Take(NodeValue::kSamples).toSamples();
|
||||
ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b);
|
||||
|
||||
if (samples_from_this_block.is_allocated()) {
|
||||
// If this is a clip, we might have extra speed/reverse information
|
||||
if (clip_cast) {
|
||||
double speed_value = clip_cast->speed();
|
||||
bool reversed = clip_cast->reverse();
|
||||
|
||||
if (qIsNull(speed_value)) {
|
||||
// Just silence, don't think there's any other practical application of 0 speed audio
|
||||
samples_from_this_block.silence();
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
if (clip_cast->maintain_audio_pitch()) {
|
||||
AudioProcessor processor;
|
||||
|
||||
if (processor.Open(samples_from_this_block.audio_params(), samples_from_this_block.audio_params(), speed_value)) {
|
||||
AudioProcessor::Buffer out;
|
||||
|
||||
// FIXME: This is not the best way to do this, the TempoProcessor works best
|
||||
// when it's given a continuous stream of audio, which is challenging
|
||||
// in our current "modular" audio system. This should still work reasonably
|
||||
// well on export (assuming audio is all generated at once on export), but
|
||||
// users may hear clicks and pops in the audio during preview due to this
|
||||
// approach.
|
||||
int r = processor.Convert(samples_from_this_block.to_raw_ptrs().data(), samples_from_this_block.sample_count(), nullptr);
|
||||
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to change tempo of audio:" << r;
|
||||
} else {
|
||||
processor.Flush();
|
||||
|
||||
processor.Convert(nullptr, 0, &out);
|
||||
|
||||
if (!out.empty()) {
|
||||
int nb_samples = out.front().size() * samples_from_this_block.audio_params().bytes_per_sample_per_channel();
|
||||
|
||||
if (nb_samples) {
|
||||
SampleBuffer new_samples(samples_from_this_block.audio_params(), nb_samples);
|
||||
|
||||
for (int i=0; i<out.size(); i++) {
|
||||
memcpy(new_samples.data(i), out[i].data(), out[i].size());
|
||||
}
|
||||
|
||||
samples_from_this_block = new_samples;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Multiply time
|
||||
samples_from_this_block.speed(speed_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
samples_from_this_block.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 copy_length = qMin(max_dest_sz, qint64(samples_from_this_block.sample_count()));
|
||||
|
||||
// Copy samples into destination buffer
|
||||
for (int i=0; i<samples_from_this_block.audio_params().channel_count(); i++) {
|
||||
block_range_buffer.set(i, samples_from_this_block.data(i), destination_offset, copy_length);
|
||||
}
|
||||
|
||||
NodeValueTable::Merge({merged_table, table});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), this);
|
||||
*/
|
||||
}
|
||||
|
||||
void Track::BlockLengthChanged()
|
||||
{
|
||||
// Assumes sender is a Block
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#define TRACK_H
|
||||
|
||||
#include "node/block/block.h"
|
||||
#include "timeline/timelinecommon.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -55,6 +54,9 @@ public:
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual ActiveElements GetActiveElementsAtTime(const QString &input, const TimeRange &r) const override;
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
|
||||
|
||||
virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
|
||||
@@ -314,30 +316,6 @@ public:
|
||||
*/
|
||||
Block* NearestBlockAfter(const rational& time) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the block that should be rendered/visible at a given time
|
||||
*
|
||||
* Use this for any video rendering or determining which block will actually be active at any
|
||||
* time.
|
||||
*
|
||||
* @return Catches the first block that matches `block.in <= time && block.out > time`. Returns
|
||||
* nullptr if the time exceeds the track length, the block active at this time is disabled, or
|
||||
* if IsMuted() is true.
|
||||
*/
|
||||
Block* BlockAtTime(const rational& time) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a list of blocks that should be rendered/visible during a given time range
|
||||
*
|
||||
* Use this for audio rendering to determine all blocks that will be active throughout a range
|
||||
* of time.
|
||||
*
|
||||
* @return Similar to BlockAtTime() but will match several blocks where
|
||||
* `block.in < range.out && block.out > range.in`. Returns an empty list if IsMuted() or if
|
||||
* `range.in >= track.length`. Blocks that are not enabled will be omitted from the returned list.
|
||||
*/
|
||||
QVector<Block*> BlocksAtTimeRange(const TimeRange& range) const;
|
||||
|
||||
const QVector<Block *> &Blocks() const
|
||||
{
|
||||
return blocks_;
|
||||
@@ -345,6 +323,12 @@ public:
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override;
|
||||
|
||||
Block *VisibleBlockAtTime(const rational &t) const
|
||||
{
|
||||
int index = GetBlockIndexAtTime(t);
|
||||
return (index == -1) ? nullptr : blocks_.at(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
|
||||
*/
|
||||
@@ -467,6 +451,10 @@ private:
|
||||
|
||||
int GetCacheIndexFromArrayIndex(int index) const;
|
||||
|
||||
int GetBlockIndexAtTime(const rational &time) const;
|
||||
|
||||
void ProcessAudioTrack(NodeValueTable *table, const TimeRange &range) const;
|
||||
|
||||
TimeRangeList block_length_pending_invalidations_;
|
||||
|
||||
QVector<Block*> blocks_;
|
||||
|
||||
+23
-39
@@ -195,10 +195,6 @@ TexturePtr NodeTraverser::GetMainTextureFromJob(const GenerateJob &job)
|
||||
|
||||
NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range)
|
||||
{
|
||||
if (!node->IsInputActiveAtTime(input, -1, range)) {
|
||||
return NodeValueTable();
|
||||
}
|
||||
|
||||
// If input is connected, retrieve value directly
|
||||
if (node->IsInputConnected(input)) {
|
||||
|
||||
@@ -220,19 +216,15 @@ NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& inpu
|
||||
// Value is an array, we will return a list of NodeValueTables
|
||||
NodeValueTableArray array_tbl;
|
||||
|
||||
int sz = node->InputArraySize(input);
|
||||
for (int i=0; i<sz; i++) {
|
||||
if (node->IsInputActiveAtTime(input, i, range)) {
|
||||
NodeValueTable& sub_tbl = array_tbl[i];
|
||||
TimeRange adjusted_range = node->InputTimeAdjustment(input, i, range);
|
||||
|
||||
if (node->IsInputConnected(input, i)) {
|
||||
Node *output = node->GetConnectedOutput(input, i);
|
||||
sub_tbl = GenerateTable(output, adjusted_range, node);
|
||||
} else {
|
||||
QVariant input_value = node->GetValueAtTime(input, adjusted_range.in(), i);
|
||||
sub_tbl.Push(node->GetInputDataType(input), input_value, node);
|
||||
}
|
||||
Node::ActiveElements a = node->GetActiveElementsAtTime(input, range);
|
||||
if (a.mode() == Node::ActiveElements::kAllElements) {
|
||||
int sz = node->InputArraySize(input);
|
||||
for (int i=0; i<sz; i++) {
|
||||
ProcessInputElement(array_tbl, node, input, i, range);
|
||||
}
|
||||
} else if (a.mode() == Node::ActiveElements::kSpecified) {
|
||||
for (int ele : a.elements()) {
|
||||
ProcessInputElement(array_tbl, node, input, ele, range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +246,20 @@ NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& inpu
|
||||
}
|
||||
}
|
||||
|
||||
void NodeTraverser::ProcessInputElement(NodeValueTableArray &array_tbl, const Node *node, const QString &input, int element, const TimeRange &range)
|
||||
{
|
||||
NodeValueTable& sub_tbl = array_tbl[element];
|
||||
TimeRange adjusted_range = node->InputTimeAdjustment(input, element, range);
|
||||
|
||||
if (node->IsInputConnected(input, element)) {
|
||||
Node *output = node->GetConnectedOutput(input, element);
|
||||
sub_tbl = GenerateTable(output, adjusted_range, node);
|
||||
} else {
|
||||
QVariant input_value = node->GetValueAtTime(input, adjusted_range.in(), element);
|
||||
sub_tbl.Push(node->GetInputDataType(input), input_value, node);
|
||||
}
|
||||
}
|
||||
|
||||
NodeTraverser::NodeTraverser() :
|
||||
cancel_(nullptr),
|
||||
transform_(nullptr),
|
||||
@@ -278,12 +284,6 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang
|
||||
// NOTE: Times how long a node takes to process, useful for profiling.
|
||||
//GTTTime gtt(n);Q_UNUSED(gtt);
|
||||
|
||||
const Track* track = dynamic_cast<const Track*>(n);
|
||||
if (track) {
|
||||
// If the range is not wholly contained in this Block, we'll need to do some extra processing
|
||||
return GenerateBlockTable(track, range);
|
||||
}
|
||||
|
||||
// FIXME: Cache certain values here if we've already processed them before
|
||||
|
||||
// Generate row for node
|
||||
@@ -338,22 +338,6 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang
|
||||
}
|
||||
}
|
||||
|
||||
NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeRange &range)
|
||||
{
|
||||
// By default, just follow the in point
|
||||
Block* active_block = track->BlockAtTime(range.in());
|
||||
|
||||
NodeValueTable table;
|
||||
|
||||
if (active_block) {
|
||||
block_stack_.push_back(active_block);
|
||||
table = GenerateTable(active_block, Track::TransformRangeForBlock(active_block, range), track);
|
||||
block_stack_.pop_back();
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
TexturePtr NodeTraverser::ProcessVideoCacheJob(const CacheJob &val)
|
||||
{
|
||||
return nullptr;
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
protected:
|
||||
NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range);
|
||||
|
||||
virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange& range);
|
||||
void ProcessInputElement(NodeValueTableArray &array_tbl, const Node *node, const QString &input, int element, const TimeRange &range);
|
||||
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time){}
|
||||
|
||||
|
||||
@@ -300,112 +300,6 @@ void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx, Deco
|
||||
p.Run();
|
||||
}
|
||||
|
||||
NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const TimeRange &range)
|
||||
{
|
||||
if (track->type() == Track::kAudio) {
|
||||
|
||||
const AudioParams& audio_params = GetCacheAudioParams();
|
||||
|
||||
QVector<Block*> active_blocks = track->BlocksAtTimeRange(range);
|
||||
|
||||
// All these blocks will need to output to a buffer so we create one here
|
||||
SampleBuffer block_range_buffer(audio_params, range.length());
|
||||
block_range_buffer.silence();
|
||||
|
||||
NodeValueTable merged_table;
|
||||
|
||||
// Loop through active blocks retrieving their audio
|
||||
foreach (Block* b, active_blocks) {
|
||||
if (dynamic_cast<ClipBlock*>(b) || dynamic_cast<TransitionBlock*>(b)) {
|
||||
TimeRange range_for_block(qMax(b->in(), range.in()),
|
||||
qMin(b->out(), range.out()));
|
||||
|
||||
qint64 destination_offset = audio_params.time_to_samples(range_for_block.in() - range.in());
|
||||
qint64 max_dest_sz = audio_params.time_to_samples(range_for_block.length());
|
||||
|
||||
// Destination buffer
|
||||
NodeValueTable table = GenerateTable(b, Track::TransformRangeForBlock(b, range_for_block));
|
||||
SampleBuffer samples_from_this_block = table.Take(NodeValue::kSamples).toSamples();
|
||||
ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b);
|
||||
|
||||
if (samples_from_this_block.is_allocated()) {
|
||||
// If this is a clip, we might have extra speed/reverse information
|
||||
if (clip_cast) {
|
||||
double speed_value = clip_cast->speed();
|
||||
bool reversed = clip_cast->reverse();
|
||||
|
||||
if (qIsNull(speed_value)) {
|
||||
// Just silence, don't think there's any other practical application of 0 speed audio
|
||||
samples_from_this_block.silence();
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
if (clip_cast->maintain_audio_pitch()) {
|
||||
AudioProcessor processor;
|
||||
|
||||
if (processor.Open(samples_from_this_block.audio_params(), samples_from_this_block.audio_params(), speed_value)) {
|
||||
AudioProcessor::Buffer out;
|
||||
|
||||
// FIXME: This is not the best way to do this, the TempoProcessor works best
|
||||
// when it's given a continuous stream of audio, which is challenging
|
||||
// in our current "modular" audio system. This should still work reasonably
|
||||
// well on export (assuming audio is all generated at once on export), but
|
||||
// users may hear clicks and pops in the audio during preview due to this
|
||||
// approach.
|
||||
int r = processor.Convert(samples_from_this_block.to_raw_ptrs().data(), samples_from_this_block.sample_count(), nullptr);
|
||||
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to change tempo of audio:" << r;
|
||||
} else {
|
||||
processor.Flush();
|
||||
|
||||
processor.Convert(nullptr, 0, &out);
|
||||
|
||||
if (!out.empty()) {
|
||||
int nb_samples = out.front().size() * samples_from_this_block.audio_params().bytes_per_sample_per_channel();
|
||||
|
||||
if (nb_samples) {
|
||||
SampleBuffer new_samples(samples_from_this_block.audio_params(), nb_samples);
|
||||
|
||||
for (int i=0; i<out.size(); i++) {
|
||||
memcpy(new_samples.data(i), out[i].data(), out[i].size());
|
||||
}
|
||||
|
||||
samples_from_this_block = new_samples;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Multiply time
|
||||
samples_from_this_block.speed(speed_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
samples_from_this_block.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 copy_length = qMin(max_dest_sz, qint64(samples_from_this_block.sample_count()));
|
||||
|
||||
// Copy samples into destination buffer
|
||||
for (int i=0; i<samples_from_this_block.audio_params().channel_count(); i++) {
|
||||
block_range_buffer.set(i, samples_from_this_block.data(i), destination_offset, copy_length);
|
||||
}
|
||||
|
||||
NodeValueTable::Merge({merged_table, table});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
merged_table.Push(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), track);
|
||||
|
||||
return merged_table;
|
||||
|
||||
} else {
|
||||
return super::GenerateBlockTable(track, range);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time)
|
||||
{
|
||||
if (ticket_->property("type").value<RenderManager::TicketType>() != RenderManager::kTypeVideo) {
|
||||
|
||||
@@ -42,8 +42,6 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange &range) override;
|
||||
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override;
|
||||
|
||||
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time) override;
|
||||
|
||||
@@ -178,7 +178,7 @@ bool TransitionTool::GetBlocksAtCoord(const TimelineCoordinate &coord, ClipBlock
|
||||
return false;
|
||||
}
|
||||
|
||||
Block* block_at_time = t->BlockAtTime(coord.GetFrame());
|
||||
Block* block_at_time = t->NearestBlockBeforeOrAt(coord.GetFrame());
|
||||
if (!dynamic_cast<ClipBlock*>(block_at_time)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1056,7 +1056,7 @@ void ViewerDisplayWidget::DrawSubtitleTracks()
|
||||
for (int j=subtitle_tracklist.size()-1; j>=0; j--) {
|
||||
Track *sub_track = subtitle_tracklist.at(j);
|
||||
if (!sub_track->IsMuted()) {
|
||||
if (SubtitleBlock *sub = dynamic_cast<SubtitleBlock*>(sub_track->BlockAtTime(time_))) {
|
||||
if (SubtitleBlock *sub = dynamic_cast<SubtitleBlock*>(sub_track->VisibleBlockAtTime(time_))) {
|
||||
// Split into lines
|
||||
QStringList list = QtUtils::WordWrapString(sub->GetText(), fm, bounding_box.width());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user