implement entire project in nodes

Project items are now represented directly in nodes opening up more versatility and possibilities. This is the first iteration of this and will be buggy. Need to test thoroughly.
This commit is contained in:
itsmattkc
2021-02-15 21:31:57 +11:00
parent 155470bec1
commit d7c5ac4b44
174 changed files with 3515 additions and 4658 deletions
+40 -84
View File
@@ -22,7 +22,6 @@
#include <QCoreApplication>
#include <QDebug>
#include <QFileInfo>
#include "codec/ffmpeg/ffmpegdecoder.h"
#include "codec/oiio/oiiodecoder.h"
@@ -43,18 +42,19 @@ QMutex Decoder::currently_conforming_mutex_;
QWaitCondition Decoder::currently_conforming_wait_cond_;
QVector<Decoder::CurrentlyConforming> Decoder::currently_conforming_;
Decoder::Decoder() :
stream_(nullptr)
const rational Decoder::kAnyTimecode = RATIONAL_MIN;
Decoder::Decoder()
{
}
bool Decoder::Open(Stream *fs)
bool Decoder::Open(const CodecStream &stream)
{
QMutexLocker locker(&mutex_);
if (stream_) {
if (stream_.IsValid()) {
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
if (stream_ == fs) {
if (stream_ == stream) {
return true;
} else {
qWarning() << "Tried to open a decoder that was already open with another stream";
@@ -62,27 +62,29 @@ bool Decoder::Open(Stream *fs)
}
} else {
// Stream was not open, try opening it now
if (fs == nullptr) {
if (!stream.IsValid()) {
// Cannot open null stream
qCritical() << "Decoder attempted to open null stream";
return false;
}
if (fs->footage()->decoder() != id()) {
qCritical() << "Tried to open footage in incorrect decoder";
if (!stream.Exists()) {
// Cannot open file that doesn't exist
qCritical() << "Decoder attempted to open file that doesn't exist";
return false;
}
// Set stream
stream_ = fs;
stream_ = stream;
// Try open internal
if (OpenInternal()) {
return true;
} else {
// Unset stream
qCritical() << "Failed to open" << stream_.filename() << "stream" << stream_.stream();
CloseInternal();
stream_ = nullptr;
stream_.Reset();
return false;
}
}
@@ -92,7 +94,7 @@ FramePtr Decoder::RetrieveVideo(const rational &timecode, const int &divider)
{
QMutexLocker locker(&mutex_);
if (!stream_) {
if (!stream_.IsValid()) {
qCritical() << "Can't retrieve video on a closed decoder";
return nullptr;
}
@@ -102,19 +104,14 @@ FramePtr Decoder::RetrieveVideo(const rational &timecode, const int &divider)
return nullptr;
}
if (stream_->type() != Stream::kVideo) {
qCritical() << "Tried to retrieve video from a non-video stream";
return nullptr;
}
return RetrieveVideoInternal(timecode, divider);
}
SampleBufferPtr Decoder::RetrieveAudio(const TimeRange &range, const AudioParams &params, const QAtomicInt *cancelled)
SampleBufferPtr Decoder::RetrieveAudio(const TimeRange &range, const AudioParams &params, const QString& cache_path, const QAtomicInt *cancelled)
{
QMutexLocker locker(&mutex_);
if (!stream_) {
if (!stream_.IsValid()) {
qCritical() << "Can't retrieve audio on a closed decoder";
return nullptr;
}
@@ -124,13 +121,8 @@ SampleBufferPtr Decoder::RetrieveAudio(const TimeRange &range, const AudioParams
return nullptr;
}
if (stream_->type() != Stream::kAudio) {
qCritical() << "Tried to retrieve audio from a non-audio stream";
return nullptr;
}
// Determine if we already have a conformed version
QString conform_filename = GetConformedFilename(params);
QString conform_filename = GetConformedFilename(cache_path, params);
CurrentlyConforming want_conform = {stream_, params};
currently_conforming_mutex_.lock();
@@ -179,9 +171,9 @@ void Decoder::Close()
{
QMutexLocker locker(&mutex_);
if (stream_) {
if (stream_.IsValid()) {
CloseInternal();
stream_ = nullptr;
stream_.Reset();
} else {
qWarning() << "Tried to close a decoder that wasn't open";
}
@@ -191,7 +183,7 @@ void Decoder::Close()
* DECODER STATIC PUBLIC MEMBERS
*/
QVector<DecoderPtr> ReceiveListOfAllDecoders()
QVector<DecoderPtr> Decoder::ReceiveListOfAllDecoders()
{
QVector<DecoderPtr> decoders;
@@ -203,55 +195,6 @@ QVector<DecoderPtr> ReceiveListOfAllDecoders()
return decoders;
}
Footage* Decoder::Probe(Project* project, const QString &filename, const QAtomicInt* cancelled)
{
// Check for a valid filename
if (filename.isEmpty()) {
qWarning() << "Tried to probe media with an empty filename";
return nullptr;
}
// Check file exists
if (!QFileInfo::exists(filename)) {
qWarning() << "Tried to probe file that doesn't exist:" << filename;
return nullptr;
}
// Create list to iterate through
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
// Pass Footage through each Decoder's probe function
for (int i=0;i<decoder_list.size();i++) {
if (cancelled && *cancelled) {
return nullptr;
}
DecoderPtr decoder = decoder_list.at(i);
Footage* footage = decoder->Probe(filename, cancelled);
if (footage) {
QFileInfo file_info(filename);
footage->set_name(file_info.fileName());
footage->set_filename(filename);
footage->set_decoder(decoder->id());
footage->set_project(project);
footage->set_timestamp(file_info.lastModified().toMSecsSinceEpoch());
footage->SetValid();
// FIXME: Cache the results so we don't have to probe if this media is added a second time
return footage;
}
}
// We aren't able to use this Footage
return nullptr;
}
DecoderPtr Decoder::CreateFromID(const QString &id)
{
if (id.isEmpty()) {
@@ -270,9 +213,12 @@ DecoderPtr Decoder::CreateFromID(const QString &id)
return nullptr;
}
QString Decoder::GetConformedFilename(const AudioParams &params)
QString Decoder::GetConformedFilename(const QString& cache_path, const AudioParams &params)
{
QString index_fn = GetIndexFilename();
QString index_fn = QStringLiteral("%1.%2:%3").arg(FileFunctions::GetUniqueFileIdentifier(stream_.filename()),
QString::number(stream_.stream()));
index_fn = QDir(cache_path).filePath(index_fn);
index_fn.append('.');
index_fn.append(QString::number(params.sample_rate()));
@@ -284,15 +230,20 @@ QString Decoder::GetConformedFilename(const AudioParams &params)
return index_fn;
}
QString Decoder::GetIndexFilename()
int64_t Decoder::GetTimeInTimebaseUnits(const rational &time, const rational &timebase, int64_t start_time)
{
return QDir(stream_->footage()->project()->cache_path()).filePath(FileFunctions::GetUniqueFileIdentifier(stream()->footage()->filename()).append(QString::number(stream()->index())));
return Timecode::time_to_timestamp(time, timebase) + start_time;
}
void Decoder::SignalProcessingProgress(const int64_t &ts)
Decoder::CodecStream Decoder::GetCodecStreamFromStreamReference(const Footage::StreamReference &ref)
{
if (stream()->duration() != AV_NOPTS_VALUE && stream()->duration() != 0) {
emit IndexProgress(static_cast<double>(ts) / static_cast<double>(stream()->duration()));
return CodecStream(ref.footage()->filename(), ref.footage()->GetRealStreamIndex(ref));
}
void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration)
{
if (duration != AV_NOPTS_VALUE && duration != 0) {
emit IndexProgress(static_cast<double>(ts) / static_cast<double>(duration));
}
}
@@ -377,4 +328,9 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filenam
return nullptr;
}
uint qHash(Decoder::CodecStream stream, uint seed)
{
return qHash(stream.filename(), seed) ^ qHash(stream.stream(), seed);
}
}
+71 -31
View File
@@ -25,6 +25,7 @@ extern "C" {
#include <libswresample/swresample.h>
}
#include <QFileInfo>
#include <QMutex>
#include <QObject>
#include <QWaitCondition>
@@ -35,6 +36,7 @@ extern "C" {
#include "codec/waveoutput.h"
#include "common/rational.h"
#include "project/item/footage/footage.h"
#include "project/item/footage/stream.h"
namespace olive {
@@ -77,6 +79,57 @@ public:
virtual bool SupportsVideo(){return false;}
virtual bool SupportsAudio(){return false;}
class CodecStream
{
public:
CodecStream() :
stream_(-1)
{
}
CodecStream(const QString& filename, int stream) :
filename_(filename),
stream_(stream)
{
}
bool IsValid() const
{
return !filename_.isEmpty() && stream_ >= 0;
}
bool Exists() const
{
return QFileInfo::exists(filename_);
}
void Reset()
{
*this = CodecStream();
}
bool operator==(const CodecStream& rhs) const
{
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
}
const QString& filename() const
{
return filename_;
}
int stream() const
{
return stream_;
}
private:
QString filename_;
int stream_;
};
/**
* @brief Open stream for decoding
*
@@ -86,7 +139,9 @@ public:
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't
* be opened OR if already open and the stream is NOT the same.
*/
bool Open(Stream* fs);
bool Open(const CodecStream& stream);
static const rational kAnyTimecode;
/**
* @brief Retrieves a video frame from footage
@@ -108,28 +163,7 @@ public:
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
SampleBufferPtr RetrieveAudio(const TimeRange& range, const AudioParams& params, const QAtomicInt *cancelled);
/**
* @brief Try to probe a Footage file by passing it through all available Decoders
*
* This is a helper function designed to abstract the process of communicating with several Decoders from the rest of
* the application. This function will take a Footage file and manually pass it through the available Decoders' Probe()
* functions until one indicates that it can decode this file. That Decoder will then dump information about the file
* into the Footage object for use throughout the program.
*
* Probing may be a lengthy process and it's recommended to run this in a separate thread.
*
* @param f
*
* A Footage object with a valid filename. If the Footage does not have a valid filename (e.g. is empty or file doesn't
* exist), this function will return FALSE.
*
* @return
*
* TRUE if a Decoder was successfully able to parse and probe this file. FALSE if not.
*/
static Footage *Probe(Project *project, const QString& filename, const QAtomicInt *cancelled);
SampleBufferPtr RetrieveAudio(const TimeRange& range, const AudioParams& params, const QString &cache_path, const QAtomicInt *cancelled);
/**
* @brief Generate a Footage object from a file
@@ -142,7 +176,7 @@ public:
*
* This function is re-entrant.
*/
virtual Footage *Probe(const QString& filename, const QAtomicInt* cancelled) const = 0;
virtual Streams Probe(const QString& filename, const QAtomicInt* cancelled) const = 0;
/**
* @brief Closes media/deallocates memory
@@ -166,6 +200,10 @@ public:
static int64_t GetImageSequenceIndex(const QString& filename);
static QVector<DecoderPtr> ReceiveListOfAllDecoders();
static CodecStream GetCodecStreamFromStreamReference(const Footage::StreamReference& ref);
protected:
/**
* @brief Internal open function
@@ -199,17 +237,15 @@ protected:
virtual bool ConformAudioInternal(const QString& filename, const AudioParams &params, const QAtomicInt* cancelled);
void SignalProcessingProgress(const int64_t& ts);
void SignalProcessingProgress(int64_t ts, int64_t duration);
/**
* @brief Get the destination filename of an audio stream conformed to a set of parameters
*/
QString GetConformedFilename(const AudioParams &params);
QString GetIndexFilename();
QString GetConformedFilename(const QString &cache_path, const AudioParams &params);
struct CurrentlyConforming {
Stream* stream;
CodecStream stream;
AudioParams params;
bool operator==(const CurrentlyConforming& rhs) const
@@ -223,11 +259,13 @@ protected:
*
* This function is NOT thread safe and should therefore only be called by thread safe functions.
*/
Stream* stream() const
const CodecStream& stream() const
{
return stream_;
}
static int64_t GetTimeInTimebaseUnits(const rational& time, const rational& timebase, int64_t start_time);
static QMutex currently_conforming_mutex_;
static QWaitCondition currently_conforming_wait_cond_;
static QVector<CurrentlyConforming> currently_conforming_;
@@ -242,12 +280,14 @@ signals:
private:
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range);
Stream* stream_;
CodecStream stream_;
QMutex mutex_;
};
uint qHash(Decoder::CodecStream stream, uint seed = 0);
}
Q_DECLARE_METATYPE(olive::Decoder::RetrieveState)
+105 -141
View File
@@ -64,13 +64,13 @@ FFmpegDecoder::~FFmpegDecoder()
bool FFmpegDecoder::OpenInternal()
{
if (instance_.Open(stream()->footage()->filename().toUtf8(), stream()->index())) {
if (instance_.Open(stream().filename().toUtf8(), stream().stream())) {
AVStream* s = instance_.avstream();
// Store one second in the source's timebase
second_ts_ = qRound64(av_q2d(av_inv_q(s->time_base)));
if (stream()->type() == Stream::kVideo) {
if (s->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
// Get an Olive compatible AVPixelFormat
ideal_pix_fmt_ = FFmpegUtils::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(s->codecpar->format));
@@ -92,20 +92,18 @@ bool FFmpegDecoder::OpenInternal()
return false;
}
FramePtr FFmpegDecoder::RetrieveStillImage(const rational &timecode, const int &divider)
/*FramePtr FFmpegDecoder::RetrieveStillImage(const rational &timecode, const int &divider)
{
// This is a still image
VideoStream* is = static_cast<VideoStream*>(stream());
QString img_filename = stream()->footage()->filename();
QString img_filename = stream().filename();
int64_t ts;
// If it's an image sequence, we'll probably need to transform the filename
if (is->video_type() == VideoStream::kVideoTypeImageSequence) {
ts = static_cast<VideoStream*>(stream())->get_time_in_timebase_units(timecode);
if (stream().GetStream().video_type() == Stream::kVideoTypeImageSequence) {
ts = stream().GetTimeInTimebaseUnits(timecode);
img_filename = TransformImageSequenceFileName(stream()->footage()->filename(), ts);
img_filename = TransformImageSequenceFileName(stream().filename(), ts);
} else {
ts = 0;
}
@@ -115,19 +113,21 @@ FramePtr FFmpegDecoder::RetrieveStillImage(const rational &timecode, const int &
FramePtr output_frame = nullptr;
Instance i;
i.Open(img_filename.toUtf8(), stream()->index());
i.Open(img_filename.toUtf8(), stream().GetRealStreamIndex());
int ret = i.GetFrame(pkt, frame);
if (ret >= 0) {
VideoParams video_params = stream().video_params();
// Create frame to return
output_frame = Frame::Create();
output_frame->set_video_params(VideoParams(frame->width,
frame->height,
native_pix_fmt_,
native_channel_count_,
is->pixel_aspect_ratio(),
is->interlacing(),
video_params.pixel_aspect_ratio(),
video_params.interlacing(),
divider));
output_frame->set_timestamp(timecode);
output_frame->allocate();
@@ -146,59 +146,48 @@ FramePtr FFmpegDecoder::RetrieveStillImage(const rational &timecode, const int &
av_packet_free(&pkt);
return output_frame;
}
}*/
FramePtr FFmpegDecoder::RetrieveVideoInternal(const rational &timecode, const int &divider)
{
VideoStream* vs = static_cast<VideoStream*>(stream());
if (scale_divider_ != divider) {
FreeScaler();
InitScaler(divider);
}
if (vs->video_type() == VideoStream::kVideoTypeStill
|| vs->video_type() == VideoStream::kVideoTypeImageSequence) {
AVStream* s = instance_.avstream();
return RetrieveStillImage(timecode, divider);
int divided_width = VideoParams::GetScaledDimension(s->codecpar->width, divider);
int divided_height = VideoParams::GetScaledDimension(s->codecpar->height, divider);
} else {
if (pool_.width() != divided_width || pool_.height() != divided_height) {
// Clear all instance queues
ClearFrameCache();
int64_t target_ts = vs->get_time_in_timebase_units(timecode);
// Set new frame pool parameters
pool_.SetParameters(divided_width, divided_height, native_pix_fmt_, native_channel_count_);
}
int divided_width = VideoParams::GetScaledDimension(vs->width(), divider);
int divided_height = VideoParams::GetScaledDimension(vs->height(), divider);
// Retrieve frame
FFmpegFramePool::ElementPtr return_frame = RetrieveFrame(timecode, divider);
if (pool_.width() != divided_width || pool_.height() != divided_height) {
// Clear all instance queues
ClearFrameCache();
// We found the frame, we'll return a copy
if (return_frame) {
FramePtr copy = Frame::Create();
copy->set_video_params(VideoParams(s->codecpar->width,
s->codecpar->height,
native_pix_fmt_,
native_channel_count_,
av_guess_sample_aspect_ratio(instance_.fmt_ctx(), s, nullptr), // May be incorrect,
VideoParams::kInterlaceNone, // May be incorrect
divider));
copy->set_timestamp(timecode);
copy->allocate();
// Set new frame pool parameters
pool_.SetParameters(divided_width, divided_height, native_pix_fmt_, native_channel_count_);
}
// Retrieve frame
FFmpegFramePool::ElementPtr return_frame = RetrieveFrame(target_ts, divider);
// We found the frame, we'll return a copy
if (return_frame) {
FramePtr copy = Frame::Create();
copy->set_video_params(VideoParams(vs->width(),
vs->height(),
native_pix_fmt_,
native_channel_count_,
vs->pixel_aspect_ratio(),
vs->interlacing(),
divider));
copy->set_timestamp(timecode);
copy->allocate();
// This data will already match the frame
memcpy(copy->data(), return_frame->data(), copy->allocated_size());
return copy;
}
// This data will already match the frame
memcpy(copy->data(), return_frame->data(), copy->allocated_size());
return copy;
}
return nullptr;
@@ -218,19 +207,16 @@ QString FFmpegDecoder::id()
return QStringLiteral("ffmpeg");
}
Footage *FFmpegDecoder::Probe(const QString& filename, const QAtomicInt* cancelled) const
Streams FFmpegDecoder::Probe(const QString &filename, const QAtomicInt *cancelled) const
{
Q_UNUSED(cancelled)
// Return value
Streams streams;
// Variable for receiving errors from FFmpeg
int error_code;
// Result to return
Footage* footage = nullptr;
// Convert QString to a C string
QByteArray ba = filename.toUtf8();
const char* filename_c = ba.constData();
QByteArray filename_c = filename.toUtf8();
// Open file in a format context
AVFormatContext* fmt_ctx = nullptr;
@@ -244,18 +230,18 @@ Footage *FFmpegDecoder::Probe(const QString& filename, const QAtomicInt* cancell
int64_t footage_duration = fmt_ctx->duration;
QVector<Stream*> streams(fmt_ctx->nb_streams);
// Dump it into the Footage object
for (unsigned int i=0;i<fmt_ctx->nb_streams;i++) {
// FFmpeg AVStream
AVStream* avstream = fmt_ctx->streams[i];
// Our native stream class
Stream stream;
// Find decoder for this stream, if it exists we can proceed
AVCodec* decoder = avcodec_find_decoder(avstream->codecpar->codec_id);
Stream* str;
if (decoder
&& (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
|| avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)) {
@@ -274,7 +260,7 @@ Footage *FFmpegDecoder::Probe(const QString& filename, const QAtomicInt* cancell
{
Instance instance;
instance.Open(filename.toUtf8(), avstream->index);
instance.Open(filename_c, avstream->index);
// Read first frame and retrieve some metadata
if (instance.GetFrame(pkt, frame) >= 0) {
@@ -332,47 +318,35 @@ Footage *FFmpegDecoder::Probe(const QString& filename, const QAtomicInt* cancell
av_packet_free(&pkt);
}
VideoStream* video_stream = new VideoStream();
if (image_is_still) {
video_stream->set_video_type(VideoStream::kVideoTypeStill);
} else {
video_stream->set_video_type(VideoStream::kVideoTypeVideo);
video_stream->set_frame_rate(frame_rate);
video_stream->set_start_time(avstream->start_time);
}
video_stream->set_width(avstream->codecpar->width);
video_stream->set_height(avstream->codecpar->height);
video_stream->set_interlacing(interlacing);
video_stream->set_pixel_aspect_ratio(pixel_aspect_ratio);
AVPixelFormat compatible_pix_fmt = FFmpegUtils::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(avstream->codecpar->format));
video_stream->set_format(GetNativePixelFormat(compatible_pix_fmt));
video_stream->set_channel_count(GetNativeChannelCount(compatible_pix_fmt));
str = video_stream;
stream = Stream(Stream::kVideo);
stream.set_width(avstream->codecpar->width);
stream.set_height(avstream->codecpar->height);
stream.set_video_type((image_is_still) ? Stream::kVideoTypeStill : Stream::kVideoTypeVideo);
stream.set_pixel_format(GetNativePixelFormat(compatible_pix_fmt));
stream.set_channel_count(GetNativeChannelCount(compatible_pix_fmt));
stream.set_interlacing(interlacing);
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
stream.set_frame_rate(frame_rate);
stream.set_start_time(avstream->start_time);
// Defaults to false, requires user intervention if incorrect
stream.set_premultiplied_alpha(false);
} else {
// Create an audio stream object
AudioStream* audio_stream = new AudioStream();
uint64_t channel_layout = avstream->codecpar->channel_layout;
if (!channel_layout) {
channel_layout = static_cast<uint64_t>(av_get_default_channel_layout(avstream->codecpar->channels));
}
audio_stream->set_channel_layout(channel_layout);
audio_stream->set_channels(avstream->codecpar->channels);
audio_stream->set_sample_rate(avstream->codecpar->sample_rate);
if (avstream->duration == AV_NOPTS_VALUE) {
// Loop through stream until we get the whole duration
if (footage_duration == AV_NOPTS_VALUE) {
Instance instance;
instance.Open(filename.toUtf8(), avstream->index);
instance.Open(filename_c, avstream->index);
AVPacket* pkt = av_packet_alloc();
AVFrame* frame = av_frame_alloc();
@@ -396,67 +370,53 @@ Footage *FFmpegDecoder::Probe(const QString& filename, const QAtomicInt* cancell
}
}
str = audio_stream;
stream = Stream(Stream::kAudio);
stream.set_channel_layout(channel_layout);
stream.set_channel_count(avstream->codecpar->channels);
stream.set_sample_rate(avstream->codecpar->sample_rate);
}
} else {
// This is data we can't utilize at the moment, but we make a Stream object anyway to keep parity with the file
str = new Stream();
Stream::Type type;
// Set the correct codec type based on FFmpeg's result
switch (avstream->codecpar->codec_type) {
case AVMEDIA_TYPE_UNKNOWN:
str->set_type(Stream::kUnknown);
break;
case AVMEDIA_TYPE_DATA:
str->set_type(Stream::kData);
type = Stream::kData;
break;
case AVMEDIA_TYPE_SUBTITLE:
str->set_type(Stream::kSubtitle);
type = Stream::kSubtitle;
break;
case AVMEDIA_TYPE_ATTACHMENT:
str->set_type(Stream::kAttachment);
type = Stream::kAttachment;
break;
case AVMEDIA_TYPE_UNKNOWN:
default:
// Fallback to an unknown stream
str->set_type(Stream::kUnknown);
type = Stream::kUnknown;
break;
}
stream = Stream(type);
}
str->set_index(avstream->index);
str->set_timebase(avstream->time_base);
str->set_duration(avstream->duration);
stream.set_timebase(avstream->time_base);
stream.set_duration(avstream->duration);
streams.append(stream);
streams[i] = str;
}
// Check if we could pick up any streams in this file
bool found_valid_streams = false;
foreach (Stream* stream, streams) {
if (stream->type() != Stream::kUnknown) {
found_valid_streams = true;
break;
}
}
if (found_valid_streams) {
// We actually have footage we can return instead of nullptr
footage = new Footage();
// Add streams
footage->add_streams(streams);
}
}
// Free all memory
avformat_close_input(&fmt_ctx);
return footage;
return streams;
}
QString FFmpegDecoder::FFmpegError(int error_code)
@@ -549,7 +509,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
delete [] data;
}
SignalProcessingProgress(frame->pts);
SignalProcessingProgress(frame->pts, instance_.avstream()->duration);
}
wave_out.close();
@@ -677,27 +637,30 @@ void FFmpegDecoder::ClearFrameCache()
cache_at_zero_ = false;
}
FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const int64_t& target_ts, int divider)
FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const rational& time, int divider)
{
int64_t target_ts = GetTimeInTimebaseUnits(time, instance_.avstream()->time_base, instance_.avstream()->start_time);
int64_t seek_ts = target_ts;
bool still_seeking = false;
// If the frame wasn't in the frame cache, see if this frame cache is too old to use
if (cached_frames_.isEmpty()
|| (target_ts < cached_frames_.first()->timestamp() || target_ts > cached_frames_.last()->timestamp() + 2*second_ts_)) {
ClearFrameCache();
if (time != kAnyTimecode) {
// If the frame wasn't in the frame cache, see if this frame cache is too old to use
if (cached_frames_.isEmpty()
|| (target_ts < cached_frames_.first()->timestamp() || target_ts > cached_frames_.last()->timestamp() + 2*second_ts_)) {
ClearFrameCache();
instance_.Seek(seek_ts);
if (seek_ts == 0) {
cache_at_zero_ = true;
}
instance_.Seek(seek_ts);
if (seek_ts == 0) {
cache_at_zero_ = true;
}
still_seeking = true;
} else {
// Search cache for frame
FFmpegFramePool::ElementPtr cached_frame = GetFrameFromCache(target_ts);
if (cached_frame) {
return cached_frame;
still_seeking = true;
} else {
// Search cache for frame
FFmpegFramePool::ElementPtr cached_frame = GetFrameFromCache(target_ts);
if (cached_frame) {
return cached_frame;
}
}
}
@@ -786,7 +749,7 @@ FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const int64_t& target_t
cached_frames_.append(cached);
// If this is a valid frame, see if this or the frame before it are the one we need
if (cached->timestamp() == target_ts) {
if (cached->timestamp() == target_ts || time == kAnyTimecode) {
return_frame = cached;
break;
} else if (cached->timestamp() > target_ts) {
@@ -809,13 +772,14 @@ FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const int64_t& target_t
void FFmpegDecoder::InitScaler(int divider)
{
VideoStream* vs = static_cast<VideoStream*>(stream());
int src_width = instance_.avstream()->codecpar->width;
int src_height = instance_.avstream()->codecpar->height;
int scaled_width = VideoParams::GetScaledDimension(vs->width(), divider);
int scaled_height = VideoParams::GetScaledDimension(vs->height(), divider);
int scaled_width = VideoParams::GetScaledDimension(src_width, divider);
int scaled_height = VideoParams::GetScaledDimension(src_height, divider);
scale_ctx_ = sws_getContext(vs->width(),
vs->height(),
scale_ctx_ = sws_getContext(src_width,
src_height,
static_cast<AVPixelFormat>(instance_.avstream()->codecpar->format),
scaled_width,
scaled_height,
+3 -4
View File
@@ -35,7 +35,6 @@ extern "C" {
#include "codec/decoder.h"
#include "codec/waveoutput.h"
#include "ffmpegframepool.h"
#include "project/item/footage/videostream.h"
namespace olive {
@@ -57,7 +56,7 @@ public:
virtual bool SupportsVideo() override{return true;}
virtual bool SupportsAudio() override{return true;}
virtual Footage* Probe(const QString& filename, const QAtomicInt* cancelled) const override;
virtual Streams Probe(const QString &filename, const QAtomicInt *cancelled) const override;
protected:
virtual bool OpenInternal() override;
@@ -122,7 +121,7 @@ private:
void InitScaler(int divider);
void FreeScaler();
FramePtr RetrieveStillImage(const rational& timecode, const int& divider);
//FramePtr RetrieveStillImage(const rational& timecode, const int& divider);
static VideoParams::Format GetNativePixelFormat(AVPixelFormat pix_fmt);
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
@@ -135,7 +134,7 @@ private:
void ClearFrameCache();
FFmpegFramePool::ElementPtr RetrieveFrame(const int64_t &target_ts, int divider);
FFmpegFramePool::ElementPtr RetrieveFrame(const rational &time, int divider);
void RemoveFirstFrame();
+10
View File
@@ -0,0 +1,10 @@
#ifndef FOOTAGEMETA_H
#define FOOTAGEMETA_H
struct FootageData {
struct StreamData {
};
};
#endif // FOOTAGEMETA_H
+19 -53
View File
@@ -51,14 +51,16 @@ QString OIIODecoder::id()
return QStringLiteral("oiio");
}
Footage *OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancelled) const
Streams OIIODecoder::Probe(const QString &filename, const QAtomicInt* cancelled) const
{
Q_UNUSED(cancelled)
Streams streams;
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
// to open a file that it can't if it's given one
if (!FileTypeIsSupported(filename)) {
return nullptr;
return streams;
}
std::string std_filename = filename.toStdString();
@@ -66,82 +68,46 @@ Footage *OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancelled
auto in = OIIO::ImageInput::open(std_filename);
if (!in) {
return nullptr;
return streams;
}
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
// it better
if (!strcmp(in->format_name(), "FFmpeg movie")) {
return nullptr;
return streams;
}
Footage* footage = new Footage();
Stream stream(Stream::kVideo);
VideoStream* image_stream = new VideoStream();
image_stream->set_width(in->spec().width);
image_stream->set_height(in->spec().height);
image_stream->set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(in->spec().format.basetype)));
image_stream->set_channel_count(in->spec().nchannels);
image_stream->set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(in->spec()));
image_stream->set_video_type(VideoStream::kVideoTypeStill);
// Images will always have just one stream
image_stream->set_index(0);
stream.set_width(in->spec().width);
stream.set_height(in->spec().height);
stream.set_pixel_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(in->spec().format.basetype)));
stream.set_channel_count(in->spec().nchannels);
stream.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(in->spec()));
stream.set_video_type(Stream::kVideoTypeStill);
// OIIO automatically premultiplies alpha
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
// likely reduces the fidelity?
image_stream->set_premultiplied_alpha(true);
stream.set_premultiplied_alpha(true);
// Get stats for this image and dump them into the Footage file
footage->add_stream(image_stream);
streams.append(stream);
// If we're here, we have a successful image open
in->close();
return footage;
return streams;
}
bool OIIODecoder::OpenInternal()
{
// If we can open the filename provided, assume everything is working (even if this is an image
// sequence with potentially missing frame)
if (OpenImageHandler(stream()->footage()->filename())) {
VideoStream* video_stream = static_cast<VideoStream*>(stream());
if (video_stream->video_type() == VideoStream::kVideoTypeStill) {
last_sequence_index_ = 0;
} else {
last_sequence_index_ = GetImageSequenceIndex(stream()->footage()->filename());
}
return true;
}
return false;
// If we can open the filename provided, assume everything is working
return OpenImageHandler(stream().filename());
}
FramePtr OIIODecoder::RetrieveVideoInternal(const rational &timecode, const int& divider)
{
VideoStream* video_stream = static_cast<VideoStream*>(stream());
int64_t sequence_index;
if (video_stream->video_type() == VideoStream::kVideoTypeStill) {
sequence_index = 0;
} else {
sequence_index = video_stream->get_time_in_timebase_units(timecode);
}
if (last_sequence_index_ != sequence_index) {
CloseImageHandle();
if (!OpenImageHandler(TransformImageSequenceFileName(stream()->footage()->filename(), sequence_index))) {
return nullptr;
}
last_sequence_index_ = sequence_index;
}
Q_UNUSED(timecode)
FramePtr frame = Frame::Create();
+1 -3
View File
@@ -40,7 +40,7 @@ public:
virtual bool SupportsVideo() override{return true;}
virtual Footage* Probe(const QString& filename, const QAtomicInt* cancelled) const override;
virtual Streams Probe(const QString& filename, const QAtomicInt* cancelled) const override;
protected:
virtual bool OpenInternal() override;
@@ -56,8 +56,6 @@ private:
void CloseImageHandle();
int64_t last_sequence_index_;
VideoParams::Format pix_fmt_;
int channel_count_;