use IDs for multiple decoders
This commit is contained in:
+23
-1
@@ -48,13 +48,35 @@ class Decoder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
Decoder();
|
||||
|
||||
Decoder(Stream* fs);
|
||||
|
||||
// Necessary for subclassing, it's empty
|
||||
virtual ~Decoder();
|
||||
|
||||
/**
|
||||
* @brief Deleted copy constructor
|
||||
*/
|
||||
Decoder(const Decoder& other) = delete;
|
||||
|
||||
/**
|
||||
* @brief Deleted move constructor
|
||||
*/
|
||||
Decoder(Decoder&& other) = delete;
|
||||
|
||||
/**
|
||||
* @brief Deleted copy assignment
|
||||
*/
|
||||
Decoder& operator=(const Decoder& other) = delete;
|
||||
|
||||
/**
|
||||
* @brief Deleted move assignment
|
||||
*/
|
||||
Decoder& operator=(Decoder&& other) = delete;
|
||||
|
||||
virtual QString id() = 0;
|
||||
|
||||
const Stream* stream();
|
||||
void set_stream(const Stream *fs);
|
||||
|
||||
|
||||
@@ -44,6 +44,11 @@ FFmpegDecoder::FFmpegDecoder() :
|
||||
{
|
||||
}
|
||||
|
||||
FFmpegDecoder::~FFmpegDecoder()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool FFmpegDecoder::Open()
|
||||
{
|
||||
if (open_) {
|
||||
@@ -192,27 +197,16 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt
|
||||
// Convert timecode to AVStream timebase
|
||||
int64_t target_ts = qFloor(timecode.toDouble() * rational(avstream_->time_base).flipped().toDouble());
|
||||
|
||||
// Index now if we haven't already
|
||||
if (frame_index_.isEmpty() && !LoadFrameIndex()) {
|
||||
Index();
|
||||
// Find closest actual timebase in the file
|
||||
target_ts = GetClosestTimestampInIndex(target_ts);
|
||||
|
||||
if (target_ts < 0) {
|
||||
Error(tr("Index failed to produce a valid timestamp"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Use index to find closest frame in file
|
||||
for (int i=1;i<frame_index_.size();i++) {
|
||||
if (frame_index_.at(i) > target_ts) {
|
||||
target_ts = frame_index_.at(i - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
|
||||
// Allocate and init a packet for reading encoded data
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
|
||||
// Cache FFmpeg error code returns
|
||||
ret = 0;
|
||||
int ret = 0;
|
||||
|
||||
// Set up seeking loop
|
||||
int64_t seek_ts = target_ts;
|
||||
@@ -225,7 +219,16 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt
|
||||
if (frame_->pts > target_ts || frame_->pts == AV_NOPTS_VALUE) {
|
||||
avcodec_flush_buffers(codec_ctx_);
|
||||
av_seek_frame(fmt_ctx_, avstream_->index, seek_ts, AVSEEK_FLAG_BACKWARD);
|
||||
seek_ts -= second_ts;
|
||||
|
||||
// FFmpeg doesn't always seek correctly, if we have to seek again we wrangle it into seeking back far enough
|
||||
|
||||
// If we already tried seeking to 0 though, there's nothing we can do so we error here
|
||||
if (seek_ts == 0) {
|
||||
Error(tr("FFmpeg failed to seek to the correct location"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
seek_ts = qMax(0L, seek_ts - second_ts);
|
||||
}
|
||||
|
||||
ret = GetFrame();
|
||||
@@ -307,6 +310,11 @@ void FFmpegDecoder::Close()
|
||||
open_ = false;
|
||||
}
|
||||
|
||||
QString FFmpegDecoder::id()
|
||||
{
|
||||
return "ffmpeg";
|
||||
}
|
||||
|
||||
bool FFmpegDecoder::Probe(Footage *f)
|
||||
{
|
||||
if (open_) {
|
||||
@@ -564,3 +572,22 @@ AVPixelFormat FFmpegDecoder::GetCompatiblePixelFormat(const AVPixelFormat &pix_f
|
||||
1,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts)
|
||||
{
|
||||
// Index now if we haven't already
|
||||
if (frame_index_.isEmpty() && !LoadFrameIndex()) {
|
||||
Index();
|
||||
}
|
||||
|
||||
// Use index to find closest frame in file
|
||||
for (int i=1;i<frame_index_.size();i++) {
|
||||
if (frame_index_.at(i) == ts) {
|
||||
return ts;
|
||||
} else if (frame_index_.at(i) > ts) {
|
||||
return frame_index_.at(i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -37,14 +37,20 @@ extern "C" {
|
||||
class FFmpegDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
FFmpegDecoder();
|
||||
|
||||
// Destructor
|
||||
virtual ~FFmpegDecoder() override;
|
||||
|
||||
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;
|
||||
|
||||
virtual QString id() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Handle an error
|
||||
@@ -118,6 +124,8 @@ private:
|
||||
*/
|
||||
AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt);
|
||||
|
||||
int64_t GetClosestTimestampInIndex(const int64_t& ts);
|
||||
|
||||
AVFormatContext* fmt_ctx_;
|
||||
AVCodecContext* codec_ctx_;
|
||||
AVStream* avstream_;
|
||||
|
||||
@@ -94,8 +94,18 @@ public:
|
||||
*/
|
||||
const uint8_t* const_data();
|
||||
|
||||
/**
|
||||
* @brief Allocate memory buffer to store data based on parameters
|
||||
*
|
||||
* For video frames, the width(), height(), and format() must be set for this function to work.
|
||||
*
|
||||
* If a memory buffer has been previously allocated without destroying, this function will destroy it.
|
||||
*/
|
||||
void allocate();
|
||||
|
||||
/**
|
||||
* @brief Destroy a memory buffer allocated with allocate()
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
|
||||
+63
-13
@@ -26,6 +26,22 @@
|
||||
|
||||
#include "decoder/ffmpeg/ffmpegdecoder.h"
|
||||
|
||||
QVector<Decoder*> ReceiveListOfAllDecoders() {
|
||||
QVector<Decoder*> decoders;
|
||||
|
||||
decoders.append(new FFmpegDecoder());
|
||||
|
||||
return decoders;
|
||||
}
|
||||
|
||||
void FreeListOfDecoders(const QVector<Decoder*>& decoders, Decoder* except = nullptr) {
|
||||
foreach (Decoder* d, decoders) {
|
||||
if (except == nullptr || except != d) {
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool olive::ProbeMedia(Footage *f)
|
||||
{
|
||||
// Check for a valid filename
|
||||
@@ -43,27 +59,61 @@ bool olive::ProbeMedia(Footage *f)
|
||||
// Reset Footage state for probing
|
||||
f->Clear();
|
||||
|
||||
// Create decoder instance
|
||||
FFmpegDecoder ff_dec;
|
||||
|
||||
// Create list to iterate through
|
||||
QList<Decoder*> decoder_list;
|
||||
decoder_list.append(&ff_dec);
|
||||
QVector<Decoder*> decoder_list = ReceiveListOfAllDecoders();
|
||||
|
||||
Decoder* found_decoder = nullptr;
|
||||
|
||||
// Pass Footage through each Decoder's probe function
|
||||
for (int i=0;i<decoder_list.size();i++) {
|
||||
if (decoder_list.at(i)->Probe(f)) {
|
||||
|
||||
// FIXME Some way of "attaching" the Footage to the Decoder without having to iterate through Decoders again at
|
||||
// render time?
|
||||
Decoder* decoder = decoder_list.at(i);
|
||||
|
||||
f->set_status(Footage::kReady);
|
||||
return true;
|
||||
if (decoder->Probe(f)) {
|
||||
|
||||
// FIXME: Cache the results so we don't have to probe if this media is added a second time
|
||||
|
||||
found_decoder = decoder;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We aren't able to use this Footage
|
||||
f->set_status(Footage::kInvalid);
|
||||
if (found_decoder == nullptr) {
|
||||
// We aren't able to use this Footage
|
||||
f->set_status(Footage::kInvalid);
|
||||
f->set_decoder(QString());
|
||||
} else {
|
||||
// We found a Decoder, so we can set this media as valid
|
||||
f->set_status(Footage::kReady);
|
||||
|
||||
return false;
|
||||
// Attach the successful Decoder to this Footage object
|
||||
f->set_decoder(found_decoder->id());
|
||||
}
|
||||
|
||||
FreeListOfDecoders(decoder_list);
|
||||
|
||||
return (found_decoder != nullptr);
|
||||
}
|
||||
|
||||
Decoder* olive::CreateDecoderFromID(const QString &id)
|
||||
{
|
||||
if (id.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create list to iterate through
|
||||
QVector<Decoder*> decoder_list = ReceiveListOfAllDecoders();
|
||||
|
||||
Decoder* found_decoder = nullptr;
|
||||
|
||||
foreach (Decoder* d, decoder_list) {
|
||||
if (d->id() == id) {
|
||||
found_decoder = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FreeListOfDecoders(decoder_list, found_decoder);
|
||||
|
||||
return found_decoder;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef PROBESERVER_H
|
||||
#define PROBESERVER_H
|
||||
|
||||
#include "decoder/decoder.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -46,6 +47,8 @@ namespace olive {
|
||||
*/
|
||||
bool ProbeMedia(Footage* f);
|
||||
|
||||
Decoder* CreateDecoderFromID(const QString& id);
|
||||
|
||||
}
|
||||
|
||||
#endif // PROBESERVER_H
|
||||
|
||||
+21
-20
@@ -82,26 +82,6 @@ public:
|
||||
*/
|
||||
virtual QString Description();
|
||||
|
||||
/**
|
||||
* @brief Add a parameter to this node
|
||||
*
|
||||
* The Node takes ownership of this parameter.
|
||||
*
|
||||
* This can be either an output or an input at any time. Parameters will always appear in the order they're added.
|
||||
*/
|
||||
void AddParameter(NodeParam* param);
|
||||
|
||||
/**
|
||||
* @brief Signal all dependent Nodes that anything cached between start_range and end_range is now invalid and
|
||||
* requires re-rendering
|
||||
*
|
||||
* Override this if your Node subclass keeps a cache, but call this base function at the end of the subclass function.
|
||||
* Default behavior is to relay this signal to all connected outputs, which will need to be done as to not break
|
||||
* the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can
|
||||
* call this function with transformed time and relay the signal that way.
|
||||
*/
|
||||
virtual void InvalidateCache(const rational& start_range, const rational& end_range);
|
||||
|
||||
/**
|
||||
* @brief Return the parameter at a given index
|
||||
*/
|
||||
@@ -134,6 +114,27 @@ public:
|
||||
*/
|
||||
static T* ValueToPtr(const QVariant& ptr);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Add a parameter to this node
|
||||
*
|
||||
* The Node takes ownership of this parameter.
|
||||
*
|
||||
* This can be either an output or an input at any time. Parameters will always appear in the order they're added.
|
||||
*/
|
||||
void AddParameter(NodeParam* param);
|
||||
|
||||
/**
|
||||
* @brief Signal all dependent Nodes that anything cached between start_range and end_range is now invalid and
|
||||
* requires re-rendering
|
||||
*
|
||||
* Override this if your Node subclass keeps a cache, but call this base function at the end of the subclass function.
|
||||
* Default behavior is to relay this signal to all connected outputs, which will need to be done as to not break
|
||||
* the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can
|
||||
* call this function with transformed time and relay the signal that way.
|
||||
*/
|
||||
virtual void InvalidateCache(const rational& start_range, const rational& end_range);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief The main processing function
|
||||
|
||||
@@ -101,6 +101,16 @@ Item::Type Footage::type() const
|
||||
return kFootage;
|
||||
}
|
||||
|
||||
const QString &Footage::decoder()
|
||||
{
|
||||
return decoder_;
|
||||
}
|
||||
|
||||
void Footage::set_decoder(const QString &id)
|
||||
{
|
||||
decoder_ = id;
|
||||
}
|
||||
|
||||
void Footage::ClearStreams()
|
||||
{
|
||||
if (streams_.empty()) {
|
||||
|
||||
@@ -185,6 +185,20 @@ public:
|
||||
*/
|
||||
virtual Type type() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the Decoder ID set when this Footage was probed
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A decoder ID
|
||||
*/
|
||||
const QString& decoder();
|
||||
|
||||
/**
|
||||
* @brief Used by decoders when they Probe to attach itself to this Footage
|
||||
*/
|
||||
void set_decoder(const QString& id);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Internal function to delete all Stream children and empty the array
|
||||
@@ -236,6 +250,11 @@ private:
|
||||
*/
|
||||
Status status_;
|
||||
|
||||
/**
|
||||
* @brief Internal attached decoder ID
|
||||
*/
|
||||
QString decoder_;
|
||||
|
||||
};
|
||||
|
||||
using FootagePtr = std::shared_ptr<Footage>;
|
||||
|
||||
Reference in New Issue
Block a user