proxy: began work towards creating proxy from video
This commit is contained in:
@@ -343,7 +343,7 @@ bool Decoder::HasConformedVersion(const AudioRenderingParams ¶ms)
|
||||
return index_already_matches;
|
||||
}
|
||||
|
||||
void Decoder::SignalIndexProgress(const int64_t &ts)
|
||||
void Decoder::SignalProcessingProgress(const int64_t &ts)
|
||||
{
|
||||
if (stream()->duration() != AV_NOPTS_VALUE && stream()->duration() != 0) {
|
||||
emit IndexProgress(qRound(100.0 * static_cast<double>(ts) / static_cast<double>(stream()->duration())));
|
||||
|
||||
+1
-1
@@ -245,7 +245,7 @@ signals:
|
||||
void IndexProgress(int);
|
||||
|
||||
protected:
|
||||
void SignalIndexProgress(const int64_t& ts);
|
||||
void SignalProcessingProgress(const int64_t& ts);
|
||||
|
||||
/**
|
||||
* @brief Returns the filename for the index
|
||||
|
||||
@@ -27,6 +27,7 @@ extern "C" {
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
@@ -588,8 +589,6 @@ void FFmpegDecoder::Error(const QString &s)
|
||||
|
||||
bool FFmpegDecoder::ProxyVideo(const QAtomicInt *cancelled, int divider)
|
||||
{
|
||||
return false;
|
||||
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
|
||||
|
||||
QString frame_index_file = GetIndexFilename().append('d').append(QString::number(divider));
|
||||
@@ -597,32 +596,113 @@ bool FFmpegDecoder::ProxyVideo(const QAtomicInt *cancelled, int divider)
|
||||
if (QFileInfo::exists(frame_index_file)) {
|
||||
|
||||
// A proxy of this type already exists so we can do nothing
|
||||
video_stream->append_proxy(divider);
|
||||
|
||||
} else {
|
||||
|
||||
// Iterate each frame and transcode it to EXR
|
||||
FFmpegDecoderInstance instance(stream()->footage()->filename().toUtf8(), stream()->index());
|
||||
|
||||
int ret;
|
||||
|
||||
AVPacket* pkt = av_packet_alloc();
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
|
||||
while (true) {
|
||||
ret = instance.GetFrame(pkt, frame);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == AVERROR_EOF) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
av_frame_free(&frame);
|
||||
av_packet_free(&pkt);
|
||||
video_stream->set_proxy(divider);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// Iterate each frame and transcode it to EXR
|
||||
FFmpegDecoderInstance instance(stream()->footage()->filename().toUtf8(), stream()->index());
|
||||
|
||||
int ret;
|
||||
|
||||
AVPixelFormat src_fmt = static_cast<AVPixelFormat>(instance.stream()->codecpar->format);
|
||||
AVPixelFormat ideal_fmt = FFmpegCommon::GetCompatiblePixelFormat(src_fmt);
|
||||
PixelFormat::Format native_fmt = GetNativePixelFormat(ideal_fmt);
|
||||
|
||||
int divided_width = instance.stream()->codecpar->width;
|
||||
int divided_height = instance.stream()->codecpar->height;
|
||||
|
||||
SwsContext* scaler = sws_getContext(instance.stream()->codecpar->width,
|
||||
instance.stream()->codecpar->height,
|
||||
src_fmt,
|
||||
divided_width,
|
||||
divided_height,
|
||||
ideal_fmt,
|
||||
SWS_FAST_BILINEAR,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0);
|
||||
|
||||
AVPacket* pkt = av_packet_alloc();
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
QVector<int64_t> frame_index;
|
||||
|
||||
QByteArray converted_buffer(PixelFormat::GetBufferSize(native_fmt,
|
||||
divided_width,
|
||||
divided_height),
|
||||
Qt::Uninitialized);
|
||||
|
||||
uint8_t* converted_data = reinterpret_cast<uint8_t*>(converted_buffer.data());
|
||||
int converted_linesize = PixelFormat::GetBufferSize(native_fmt,
|
||||
divided_width,
|
||||
1);
|
||||
|
||||
bool succeeded = false;
|
||||
|
||||
while (true) {
|
||||
if (cancelled && *cancelled) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = instance.GetFrame(pkt, frame);
|
||||
|
||||
// Handle errors
|
||||
if (ret < 0) {
|
||||
if (ret == AVERROR_EOF) {
|
||||
succeeded = true;
|
||||
} else {
|
||||
char err_str[50];
|
||||
av_strerror(ret, err_str, 50);
|
||||
qWarning() << "Failed to proxy:" << ret << err_str;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
sws_scale(scaler,
|
||||
frame->data,
|
||||
frame->linesize,
|
||||
0,
|
||||
frame->height,
|
||||
&converted_data,
|
||||
&converted_linesize);
|
||||
|
||||
QString dst_fn = GetIndexFilename()
|
||||
.append(QString::number(frame->pts))
|
||||
.append(QStringLiteral(".tiff"));
|
||||
|
||||
std::string dst_std_fn = dst_fn.toStdString();
|
||||
|
||||
auto out = OIIO::ImageOutput::create(dst_std_fn);
|
||||
|
||||
if (out) {
|
||||
|
||||
out->open(dst_std_fn,
|
||||
OIIO::ImageSpec(divided_width,
|
||||
divided_height,
|
||||
PixelFormat::ChannelCount(native_fmt),
|
||||
PixelFormat::GetOIIOTypeDesc(native_fmt)));
|
||||
|
||||
out->write_image(PixelFormat::GetOIIOTypeDesc(native_fmt), converted_data);
|
||||
|
||||
out->close();
|
||||
|
||||
#if OIIO_VERSION < 10903
|
||||
OIIO::ImageOutput::destroy(out);
|
||||
#endif
|
||||
}
|
||||
|
||||
frame_index.append(frame->pts);
|
||||
SignalProcessingProgress(frame->pts);
|
||||
}
|
||||
|
||||
sws_freeContext(scaler);
|
||||
|
||||
av_frame_free(&frame);
|
||||
av_packet_free(&pkt);
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool FFmpegDecoder::ConformAudio(const QAtomicInt *cancelled, const AudioRenderingParams &p)
|
||||
@@ -694,39 +774,39 @@ bool FFmpegDecoder::ConformAudio(const QAtomicInt *cancelled, const AudioRenderi
|
||||
} else {
|
||||
char err_str[50];
|
||||
av_strerror(ret, err_str, 50);
|
||||
qWarning() << "Failed to index:" << ret << err_str;
|
||||
qWarning() << "Failed to conform:" << ret << err_str;
|
||||
}
|
||||
break;
|
||||
|
||||
} else {
|
||||
// Allocate buffers
|
||||
int nb_samples = swr_get_out_samples(resampler, frame->nb_samples);
|
||||
char* data = new char[p.samples_to_bytes(nb_samples)];
|
||||
|
||||
// Resample audio to our destination parameters
|
||||
nb_samples = swr_convert(resampler,
|
||||
reinterpret_cast<uint8_t**>(&data),
|
||||
nb_samples,
|
||||
const_cast<const uint8_t**>(frame->data),
|
||||
frame->nb_samples);
|
||||
|
||||
if (nb_samples < 0) {
|
||||
char err_str[50];
|
||||
av_strerror(nb_samples, err_str, 50);
|
||||
qWarning() << "libswresample failed with error:" << nb_samples << err_str;
|
||||
break;
|
||||
}
|
||||
|
||||
// Write packed WAV data to the disk cache
|
||||
wave_out.write(data, p.samples_to_bytes(nb_samples));
|
||||
|
||||
// If we allocated an output for the resampler, delete it here
|
||||
if (data != reinterpret_cast<char*>(frame->data[0])) {
|
||||
delete [] data;
|
||||
}
|
||||
|
||||
SignalIndexProgress(frame->pts);
|
||||
}
|
||||
|
||||
// Allocate buffers
|
||||
int nb_samples = swr_get_out_samples(resampler, frame->nb_samples);
|
||||
char* data = new char[p.samples_to_bytes(nb_samples)];
|
||||
|
||||
// Resample audio to our destination parameters
|
||||
nb_samples = swr_convert(resampler,
|
||||
reinterpret_cast<uint8_t**>(&data),
|
||||
nb_samples,
|
||||
const_cast<const uint8_t**>(frame->data),
|
||||
frame->nb_samples);
|
||||
|
||||
if (nb_samples < 0) {
|
||||
char err_str[50];
|
||||
av_strerror(nb_samples, err_str, 50);
|
||||
qWarning() << "libswresample failed with error:" << nb_samples << err_str;
|
||||
break;
|
||||
}
|
||||
|
||||
// Write packed WAV data to the disk cache
|
||||
wave_out.write(data, p.samples_to_bytes(nb_samples));
|
||||
|
||||
// If we allocated an output for the resampler, delete it here
|
||||
if (data != reinterpret_cast<char*>(frame->data[0])) {
|
||||
delete [] data;
|
||||
}
|
||||
|
||||
SignalProcessingProgress(frame->pts);
|
||||
}
|
||||
|
||||
wave_out.close();
|
||||
|
||||
@@ -304,11 +304,11 @@ void Footage::ClearStreams()
|
||||
streams_.clear();
|
||||
}
|
||||
|
||||
bool Footage::HasStreamsOfType(const Stream::Type type)
|
||||
bool Footage::HasStreamsOfType(const Stream::Type &type) const
|
||||
{
|
||||
// Return true if any streams are video streams
|
||||
for (int i=0;i<streams_.size();i++) {
|
||||
if (streams_.at(i)->type() == type) {
|
||||
foreach (StreamPtr stream, streams_) {
|
||||
if (stream->type() == type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -316,6 +316,17 @@ bool Footage::HasStreamsOfType(const Stream::Type type)
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamPtr Footage::get_first_stream_of_type(const Stream::Type &type) const
|
||||
{
|
||||
foreach (StreamPtr stream, streams_) {
|
||||
if (stream->type() == type) {
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Footage::UpdateTooltip()
|
||||
{
|
||||
switch (status_) {
|
||||
|
||||
@@ -206,12 +206,6 @@ public:
|
||||
|
||||
quint64 get_enabled_stream_flags() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Internal function to delete all Stream children and empty the array
|
||||
*/
|
||||
void ClearStreams();
|
||||
|
||||
/**
|
||||
* @brief Check if this footage has streams of a certain type
|
||||
*
|
||||
@@ -219,7 +213,15 @@ private:
|
||||
*
|
||||
* The stream type to check for
|
||||
*/
|
||||
bool HasStreamsOfType(const Stream::Type type);
|
||||
bool HasStreamsOfType(const Stream::Type& type) const;
|
||||
|
||||
StreamPtr get_first_stream_of_type(const Stream::Type& type) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Internal function to delete all Stream children and empty the array
|
||||
*/
|
||||
void ClearStreams();
|
||||
|
||||
/**
|
||||
* @brief Update the icon based on the Footage status
|
||||
|
||||
@@ -102,8 +102,6 @@ protected:
|
||||
virtual void SaveCustomParameters(QXmlStreamWriter* writer) const;
|
||||
|
||||
signals:
|
||||
void IndexChanged();
|
||||
|
||||
void ParametersChanged();
|
||||
|
||||
private:
|
||||
|
||||
@@ -30,7 +30,9 @@ const int64_t VideoStream::kEndTimestamp = AV_NOPTS_VALUE;
|
||||
|
||||
VideoStream::VideoStream() :
|
||||
start_time_(0),
|
||||
is_image_sequence_(false)
|
||||
is_image_sequence_(false),
|
||||
is_generating_proxy_(false),
|
||||
using_proxy_(0)
|
||||
{
|
||||
set_type(kVideo);
|
||||
}
|
||||
@@ -73,18 +75,39 @@ void VideoStream::set_image_sequence(bool e)
|
||||
is_image_sequence_ = e;
|
||||
}
|
||||
|
||||
bool VideoStream::has_proxy(const int ÷r)
|
||||
bool VideoStream::is_generating_proxy()
|
||||
{
|
||||
QMutexLocker locker(proxy_access_lock());
|
||||
|
||||
return proxies_.contains(divider);
|
||||
return is_generating_proxy_;
|
||||
}
|
||||
|
||||
void VideoStream::append_proxy(const int ÷r)
|
||||
bool VideoStream::try_start_proxy()
|
||||
{
|
||||
QMutexLocker locker(proxy_access_lock());
|
||||
|
||||
proxies_.append(divider);
|
||||
if (is_generating_proxy_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_generating_proxy_ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int VideoStream::using_proxy()
|
||||
{
|
||||
QMutexLocker locker(proxy_access_lock());
|
||||
|
||||
return using_proxy_;
|
||||
}
|
||||
|
||||
void VideoStream::set_proxy(const int ÷r)
|
||||
{
|
||||
QMutexLocker locker(proxy_access_lock());
|
||||
|
||||
using_proxy_ = divider;
|
||||
is_generating_proxy_ = false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -61,8 +61,10 @@ public:
|
||||
bool save_frame_index(const QString& s);
|
||||
*/
|
||||
|
||||
bool has_proxy(const int& divider);
|
||||
void append_proxy(const int& divider);
|
||||
bool is_generating_proxy();
|
||||
bool try_start_proxy();
|
||||
int using_proxy();
|
||||
void set_proxy(const int& divider);
|
||||
|
||||
private:
|
||||
rational frame_rate_;
|
||||
@@ -73,7 +75,9 @@ private:
|
||||
|
||||
bool is_image_sequence_;
|
||||
|
||||
QVector<int> proxies_;
|
||||
bool is_generating_proxy_;
|
||||
|
||||
int using_proxy_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ void AudioRenderBackend::DisconnectViewer(ViewerOutput *node)
|
||||
{
|
||||
disconnect(node, &ViewerOutput::AudioChangedBetween, this, &AudioRenderBackend::InvalidateCache);
|
||||
disconnect(node, &ViewerOutput::LengthChanged, this, &AudioRenderBackend::TruncateCache);
|
||||
|
||||
conform_wait_info_.clear();
|
||||
}
|
||||
|
||||
bool AudioRenderBackend::GenerateCacheIDInternal(QCryptographicHash &hash)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(conform)
|
||||
add_subdirectory(proxy)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
|
||||
@@ -34,7 +34,7 @@ ConformTask::ConformTask(AudioStreamPtr stream, const AudioRenderingParams& para
|
||||
void ConformTask::Action()
|
||||
{
|
||||
if (stream_->footage()->decoder().isEmpty()) {
|
||||
emit Failed(QStringLiteral("Stream has no decoder"));
|
||||
emit Failed(tr("Failed to find decoder to conform audio stream"));
|
||||
} else {
|
||||
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
|
||||
|
||||
@@ -42,9 +42,11 @@ void ConformTask::Action()
|
||||
|
||||
connect(decoder.get(), &Decoder::IndexProgress, this, &ConformTask::ProgressChanged);
|
||||
|
||||
decoder->ConformAudio(&IsCancelled(), params_);
|
||||
|
||||
emit Succeeded();
|
||||
if (decoder->ConformAudio(&IsCancelled(), params_)) {
|
||||
emit Succeeded();
|
||||
} else {
|
||||
emit Failed(QStringLiteral("Failed to conform audio"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
task/proxy/proxy.h
|
||||
task/proxy/proxy.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,60 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "proxy.h"
|
||||
|
||||
#include "codec/decoder.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
ProxyTask::ProxyTask(VideoStreamPtr stream, int divider) :
|
||||
stream_(stream),
|
||||
divider_(divider)
|
||||
{
|
||||
if (divider_ == 1) {
|
||||
SetTitle(tr("Generating full resolution proxy %1:%2").arg(stream_->footage()->filename(),
|
||||
QString::number(stream_->index())));
|
||||
} else {
|
||||
SetTitle(tr("Generating 1/%1 resolution proxy %2:%3").arg(QString::number(divider),
|
||||
stream_->footage()->filename(),
|
||||
QString::number(stream_->index())));
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyTask::Action()
|
||||
{
|
||||
if (stream_->footage()->decoder().isEmpty()) {
|
||||
emit Failed(tr("Failed to find decoder to conform audio stream"));
|
||||
} else {
|
||||
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
|
||||
|
||||
decoder->set_stream(stream_);
|
||||
|
||||
connect(decoder.get(), &Decoder::IndexProgress, this, &ProxyTask::ProgressChanged);
|
||||
|
||||
if (decoder->ProxyVideo(&IsCancelled(), divider_)) {
|
||||
emit Succeeded();
|
||||
} else {
|
||||
emit Failed(QStringLiteral("Failed to generate proxy"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -0,0 +1,46 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef PROXYTASK_H
|
||||
#define PROXYTASK_H
|
||||
|
||||
#include "project/item/footage/videostream.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class ProxyTask : public Task
|
||||
{
|
||||
public:
|
||||
ProxyTask(VideoStreamPtr stream, int divider);
|
||||
|
||||
protected:
|
||||
virtual void Action() override;
|
||||
|
||||
private:
|
||||
VideoStreamPtr stream_;
|
||||
|
||||
int divider_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // PROXYTASK_H
|
||||
@@ -31,6 +31,8 @@
|
||||
#include "core.h"
|
||||
#include "dialog/footageproperties/footageproperties.h"
|
||||
#include "dialog/sequence/sequence.h"
|
||||
#include "task/proxy/proxy.h"
|
||||
#include "task/taskmanager.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
@@ -284,6 +286,36 @@ void ProjectExplorer::ShowContextMenu()
|
||||
connect(reveal_action, &QAction::triggered, this, &ProjectExplorer::RevealSelectedFootage);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
Footage* f = static_cast<Footage*>(context_menu_item_);
|
||||
|
||||
if (f->HasStreamsOfType(Stream::kVideo)) {
|
||||
Menu* proxy_menu = new Menu(tr("Proxy"), &menu);
|
||||
menu.addMenu(proxy_menu);
|
||||
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(f->get_first_stream_of_type(Stream::kVideo));
|
||||
|
||||
if (video_stream->is_generating_proxy()) {
|
||||
|
||||
// Prevent multiple proxy actions from occurring at once
|
||||
QAction* cant_proxy_action = proxy_menu->addAction(tr("Proxy being generated..."));
|
||||
cant_proxy_action->setEnabled(false);
|
||||
|
||||
} else {
|
||||
|
||||
proxy_menu->addAction(tr("(None)"))->setData(0);
|
||||
proxy_menu->addSeparator();
|
||||
proxy_menu->addAction(tr("Full"))->setData(1);
|
||||
proxy_menu->addAction(tr("1/2"))->setData(2);
|
||||
proxy_menu->addAction(tr("1/4"))->setData(4);
|
||||
proxy_menu->addAction(tr("1/8"))->setData(8);
|
||||
|
||||
connect(proxy_menu, &Menu::triggered, this, &ProjectExplorer::ContextMenuStartProxy);
|
||||
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
QAction* properties_action = menu.addAction(tr("P&roperties"));
|
||||
@@ -347,6 +379,28 @@ void ProjectExplorer::OpenContextMenuItemInNewWindow()
|
||||
Core::instance()->main_window()->FolderOpen(project(), context_menu_item_, true);
|
||||
}
|
||||
|
||||
void ProjectExplorer::ContextMenuStartProxy(QAction *a)
|
||||
{
|
||||
// Find video stream
|
||||
VideoStreamPtr video_stream = nullptr;
|
||||
|
||||
foreach (StreamPtr s, static_cast<Footage*>(context_menu_item_)->streams()) {
|
||||
if (s->type() == Stream::kVideo) {
|
||||
video_stream = std::static_pointer_cast<VideoStream>(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!video_stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (video_stream->try_start_proxy()) {
|
||||
ProxyTask* proxy_task = new ProxyTask(video_stream, a->data().toInt());
|
||||
TaskManager::instance()->AddTask(proxy_task);
|
||||
}
|
||||
}
|
||||
|
||||
Project *ProjectExplorer::project() const
|
||||
{
|
||||
return model_.project();
|
||||
|
||||
@@ -171,6 +171,8 @@ private slots:
|
||||
|
||||
void OpenContextMenuItemInNewWindow();
|
||||
|
||||
void ContextMenuStartProxy(QAction* a);
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
Reference in New Issue
Block a user