diff --git a/app/core.cpp b/app/core.cpp index 2785f1c17..70543fbbe 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -27,6 +27,7 @@ #include #include +#include "decoder/probeserver.h" #include "panel/panelfocusmanager.h" #include "panel/project/project.h" #include "project/item/footage/footage.h" @@ -152,6 +153,8 @@ void Core::ImportFiles(const QStringList &urls) f->set_name(file_info.fileName()); //file_info.lastModified(); + olive::ProbeMedia(f); + active_project->root()->add_child(f); } diff --git a/app/decoder/decoder.h b/app/decoder/decoder.h index f9a1591ed..5aac398be 100644 --- a/app/decoder/decoder.h +++ b/app/decoder/decoder.h @@ -37,6 +37,33 @@ public: Stream* stream(); void set_stream(Stream* fs); + /** + * @brief Probe a footage file and dump metadata about it + * + * When a Footage file is imported, we'll need to know whether Olive is equipped with a decoder for utilizing it + * and metadata should be retrieved about it if so. For this purpose, the Footage object is passed through all + * Probe() functions of available deocders until one returns TRUE. A FALSE return means the Decoder was unable to + * parse this file and the next should be tried. + * + * Probe() differs from Open() since it focuses on a file as a whole rather than one particular stream. Probe() + * should be able to be run directly without calling Open() or Close() and should free its memory before returning. + * + * Probe() will never be called on an object that is also used for decoding. In other words, it will never be called + * alongside Open() or Close() externally, so Probe() can use variables that would otherwise be used for decoding + * without conflict. + * + * @param f + * + * A Footage object to probe. The Footage object will have a valid filename and will be empty prior to being sent + * to this function (i.e. Footage::Clear() will not have to be called). + * + * @return + * + * TRUE if the Decoder was able to decode this file. FALSE if not. This function should have filled the Footage + * object with metadata if it returns TRUE. Otherwise, the Footage object should be untouched. + */ + virtual bool Probe(Footage* f) = 0; + /** * @brief Open media/allocate memory * diff --git a/app/decoder/ffmpeg/ffmpegdecoder.cpp b/app/decoder/ffmpeg/ffmpegdecoder.cpp index 6ff55993d..56d957fad 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.cpp +++ b/app/decoder/ffmpeg/ffmpegdecoder.cpp @@ -100,6 +100,14 @@ bool FFmpegDecoder::Open() return true; } +FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &length) +{ + Q_UNUSED(timecode) + Q_UNUSED(length) + + return nullptr; +} + void FFmpegDecoder::Close() { if (opts_ != nullptr) { @@ -120,6 +128,95 @@ void FFmpegDecoder::Close() open_ = false; } +bool FFmpegDecoder::Probe(Footage *f) +{ + int error_code; + + // Convert QString to a C strng + QByteArray ba = f->filename().toUtf8(); + const char* filename = ba.constData(); + + // Open file in a format context + error_code = avformat_open_input(&fmt_ctx_, filename, nullptr, nullptr); + + // Handle format context error + if (error_code == 0) { + + // Retrieve metadata about the media + av_dump_format(fmt_ctx_, stream()->index(), filename, 0); + + // Dump it into the Footage object + for (unsigned int i=0;inb_streams;i++) { + + avstream_ = fmt_ctx_->streams[i]; + + Stream* str; + + if (avstream_->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + + // Create a video stream object + VideoStream* video_stream = new VideoStream(); + + video_stream->set_width(avstream_->codecpar->width); + video_stream->set_height(avstream_->codecpar->height); + + str = video_stream; + + } else if (avstream_->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + + // Create an audio stream object + AudioStream* audio_stream = new AudioStream(); + + audio_stream->set_layout(avstream_->codecpar->channel_layout); + audio_stream->set_channels(avstream_->codecpar->channels); + audio_stream->set_sample_rate(avstream_->codecpar->sample_rate); + + str = audio_stream; + + } 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(); + + // 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); + break; + case AVMEDIA_TYPE_SUBTITLE: + str->set_type(Stream::kSubtitle); + break; + case AVMEDIA_TYPE_ATTACHMENT: + str->set_type(Stream::kAttachment); + break; + + // We should never realistically get here, but we make an "invalid" stream just in case + // We don't use a "default:" in case more AVMEDIA_TYPEs get introduced in later APIs that need handling + case AVMEDIA_TYPE_NB: + case AVMEDIA_TYPE_VIDEO: + case AVMEDIA_TYPE_AUDIO: + str->set_type(Stream::kUnknown); + break; + } + + } + + str->set_index(avstream_->index); + str->set_timebase(avstream_->time_base); + + f->add_stream(str); + } + } + + // Free all memory + Close(); + + return false; +} + void FFmpegDecoder::FFmpegErr(int error_code) { char err[1024]; diff --git a/app/decoder/ffmpeg/ffmpegdecoder.h b/app/decoder/ffmpeg/ffmpegdecoder.h index c690a2fdd..f0887041c 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.h +++ b/app/decoder/ffmpeg/ffmpegdecoder.h @@ -10,7 +10,10 @@ class FFmpegDecoder : public Decoder public: FFmpegDecoder(); + virtual bool Probe(Footage *f) override; + virtual bool Open() override; + virtual FramePtr Retrieve(const rational &timecode, const rational &length = 0) override; virtual void Close() override; protected: diff --git a/app/decoder/probeserver.cpp b/app/decoder/probeserver.cpp index 1de734185..9891a36d7 100644 --- a/app/decoder/probeserver.cpp +++ b/app/decoder/probeserver.cpp @@ -1,8 +1,24 @@ #include "probeserver.h" +#include +#include + #include "decoder/ffmpeg/ffmpegdecoder.h" -bool olive::Probe(Footage *f) +bool olive::ProbeMedia(Footage *f) { + // Check for a valid filename + if (f->filename().isEmpty()) { + qWarning() << QCoreApplication::translate("ProbeMedia", "Tried to probe media with an empty filename"); + return false; + } + FFmpegDecoder ff_dec; + + // Pass footage through FFmpeg's probe function + if (ff_dec.Probe(f)) { + return true; + } + + return false; } diff --git a/app/decoder/probeserver.h b/app/decoder/probeserver.h index 9ae67eb05..cc19e44dd 100644 --- a/app/decoder/probeserver.h +++ b/app/decoder/probeserver.h @@ -5,7 +5,7 @@ namespace olive { -bool Probe(Footage* f); +bool ProbeMedia(Footage* f); } diff --git a/app/project/item/footage/audiostream.cpp b/app/project/item/footage/audiostream.cpp index 349824544..20bba0449 100644 --- a/app/project/item/footage/audiostream.cpp +++ b/app/project/item/footage/audiostream.cpp @@ -22,11 +22,7 @@ AudioStream::AudioStream() { -} - -Stream::Type AudioStream::type() -{ - return kAudio; + set_type(kAudio); } const int &AudioStream::channels() @@ -39,12 +35,12 @@ void AudioStream::set_channels(const int &channels) channels_ = channels; } -const int &AudioStream::layout() +const uint64_t &AudioStream::layout() { return layout_; } -void AudioStream::set_layout(const int &layout) +void AudioStream::set_layout(const uint64_t &layout) { layout_ = layout; } diff --git a/app/project/item/footage/audiostream.h b/app/project/item/footage/audiostream.h index 7ca176d03..024e46aea 100644 --- a/app/project/item/footage/audiostream.h +++ b/app/project/item/footage/audiostream.h @@ -21,6 +21,10 @@ #ifndef AUDIOSTREAM_H #define AUDIOSTREAM_H +extern "C" { +#include +} + #include "rational.h" #include "stream.h" @@ -29,20 +33,18 @@ class AudioStream : public Stream public: AudioStream(); - virtual Type type() override; - const int& channels(); void set_channels(const int& channels); - const int& layout(); - void set_layout(const int& layout); + const uint64_t& layout(); + void set_layout(const uint64_t& layout); const int& sample_rate(); void set_sample_rate(const int& sample_rate); private: int channels_; - int layout_; + uint64_t layout_; int sample_rate_; }; diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index 492138b23..8ee418939 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -25,6 +25,20 @@ Footage::Footage() } +Footage::~Footage() +{ + ClearStreams(); +} + +void Footage::Clear() +{ + // Clear filename string + filename_.clear(); + + // Clear all streams + ClearStreams(); +} + const QString &Footage::filename() { return filename_; @@ -45,21 +59,32 @@ void Footage::set_timestamp(const QDateTime &t) timestamp_ = t; } -void Footage::add_stream(const Stream &s) +void Footage::add_stream(Stream *s) { // Add a copy of this stream to the list streams_.append(s); // Set its footage parent to this - streams_.last().set_footage(this); + streams_.last()->set_footage(this); } const Stream *Footage::stream(int index) { - return &streams_.at(index); + return streams_.at(index); } Item::Type Footage::type() const { return kFootage; } + +void Footage::ClearStreams() +{ + // Delete all streams + for (int i=0;i streams_; + QList streams_; }; #endif // FOOTAGE_H diff --git a/app/project/item/footage/stream.cpp b/app/project/item/footage/stream.cpp index 58bbcd060..aa0b31ee7 100644 --- a/app/project/item/footage/stream.cpp +++ b/app/project/item/footage/stream.cpp @@ -1,7 +1,8 @@ #include "stream.h" Stream::Stream() : - footage_(nullptr) + footage_(nullptr), + type_(kUnknown) { } @@ -10,6 +11,16 @@ Stream::~Stream() { } +const Stream::Type &Stream::type() +{ + return type_; +} + +void Stream::set_type(const Stream::Type &type) +{ + type_ = type; +} + Footage *Stream::footage() { return footage_; @@ -20,11 +31,6 @@ void Stream::set_footage(Footage *f) footage_ = f; } -Stream::Type Stream::type() -{ - return kUnknown; -} - const rational &Stream::timebase() { return timebase_; diff --git a/app/project/item/footage/stream.h b/app/project/item/footage/stream.h index efb9b563a..3a76e04a5 100644 --- a/app/project/item/footage/stream.h +++ b/app/project/item/footage/stream.h @@ -27,7 +27,8 @@ public: */ virtual ~Stream(); - virtual Type type(); + const Type& type(); + void set_type(const Type& type); Footage* footage(); void set_footage(Footage* f); @@ -45,6 +46,8 @@ private: int index_; + Type type_; + }; #endif // STREAM_H diff --git a/app/project/item/footage/videostream.cpp b/app/project/item/footage/videostream.cpp index 3633577a1..8377b0f88 100644 --- a/app/project/item/footage/videostream.cpp +++ b/app/project/item/footage/videostream.cpp @@ -22,11 +22,7 @@ VideoStream::VideoStream() { -} - -Stream::Type VideoStream::type() -{ - return kVideo; + set_type(kVideo); } const int &VideoStream::width() diff --git a/app/project/item/footage/videostream.h b/app/project/item/footage/videostream.h index 24d5c8f40..159a7bb5b 100644 --- a/app/project/item/footage/videostream.h +++ b/app/project/item/footage/videostream.h @@ -29,8 +29,6 @@ class VideoStream : public Stream public: VideoStream(); - virtual Type type() override; - const int& width(); void set_width(const int& width);