various: added hooks so that various lengthy functions could be cancelled from another thread

This commit is contained in:
itsmattkc
2020-02-17 02:10:12 +11:00
parent fd49b6eb55
commit 22046535ca
25 changed files with 208 additions and 80 deletions
+10 -6
View File
@@ -57,12 +57,12 @@ void Decoder::set_stream(StreamPtr fs)
stream_ = fs;
}
FramePtr Decoder::RetrieveVideo(const rational &/*timecode*/)
FramePtr Decoder::RetrieveVideo(const rational &/*timecode*/, const QAtomicInt* cancelled)
{
return nullptr;
}
FramePtr Decoder::RetrieveAudio(const rational &/*timecode*/, const rational &/*length*/, const AudioRenderingParams &/*params*/)
FramePtr Decoder::RetrieveAudio(const rational &/*timecode*/, const rational &/*length*/, const AudioRenderingParams &/*params*/, const QAtomicInt* cancelled)
{
return nullptr;
}
@@ -93,7 +93,7 @@ QVector<DecoderPtr> ReceiveListOfAllDecoders() {
return decoders;
}
bool Decoder::ProbeMedia(Footage *f)
bool Decoder::ProbeMedia(Footage *f, const QAtomicInt* cancelled)
{
// Check for a valid filename
if (f->filename().isEmpty()) {
@@ -116,9 +116,13 @@ bool Decoder::ProbeMedia(Footage *f)
// Pass Footage through each Decoder's probe function
for (int i=0;i<decoder_list.size();i++) {
if (cancelled && *cancelled) {
return false;
}
DecoderPtr decoder = decoder_list.at(i);
if (decoder->Probe(f)) {
if (decoder->Probe(f, cancelled)) {
// We found a Decoder, so we can set this media as valid
f->set_status(Footage::kReady);
@@ -168,13 +172,13 @@ DecoderPtr Decoder::CreateFromID(const QString &id)
return nullptr;
}
void Decoder::Conform(const AudioRenderingParams &params)
void Decoder::Conform(const AudioRenderingParams &params, const QAtomicInt* cancelled)
{
Q_UNUSED(params)
qCritical() << "Conform called on an audio decoder that does not have a handler for it:" << id();
abort();
}
void Decoder::Index()
void Decoder::Index(const QAtomicInt *cancelled)
{
}
+7 -7
View File
@@ -91,7 +91,7 @@ public:
* 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;
virtual bool Probe(Footage* f, const QAtomicInt* cancelled) = 0;
/**
* @brief Open media/allocate memory
@@ -127,7 +127,7 @@ public:
* A FramePtr of valid data at this timecode or nullptr if there was nothing to retrieve at the provided timecode or
* the media could not be opened.
*/
virtual FramePtr RetrieveVideo(const rational& timecode);
virtual FramePtr RetrieveVideo(const rational& timecode, const QAtomicInt* cancelled);
/**
* @brief Retrieve video frame
@@ -153,7 +153,7 @@ public:
* A FramePtr of valid data at this timecode of the requested length or nullptr if there was nothing to retrieve at
* the provided timecode or the media could not be opened.
*/
virtual FramePtr RetrieveAudio(const rational& timecode, const rational& length, const AudioRenderingParams& params);
virtual FramePtr RetrieveAudio(const rational& timecode, const rational& length, const AudioRenderingParams& params, const QAtomicInt* cancelled);
virtual bool SupportsVideo();
virtual bool SupportsAudio();
@@ -174,7 +174,7 @@ public:
*
* Used to determine which frame will be served at a given time, useful for caching.
*/
virtual int64_t GetTimestampFromTime(const rational& time) = 0;
virtual int64_t GetTimestampFromTime(const rational& time, const QAtomicInt* cancelled) = 0;
/**
* @brief Try to probe a Footage file by passing it through all available Decoders
@@ -195,7 +195,7 @@ public:
*
* TRUE if a Decoder was successfully able to parse and probe this file. FALSE if not.
*/
static bool ProbeMedia(Footage* f);
static bool ProbeMedia(Footage* f, const QAtomicInt *cancelled);
/**
* @brief Create a Decoder instance using a Decoder ID
@@ -217,7 +217,7 @@ public:
* All audio decoders must override this. It's not pure since video decoders don't need to use this, but default
* behavior will abort since it should never be called.
*/
virtual void Conform(const AudioRenderingParams& params);
virtual void Conform(const AudioRenderingParams& params, const QAtomicInt* cancelled);
/**
* @brief Create an index for this media
@@ -228,7 +228,7 @@ public:
* Indexing is slow so it's recommended to do it in a background thread. Index() must be called while the Decoder is
* open, and does not automatically call Open() and Close() the Decoder. The caller must call thse manually.
*/
virtual void Index();
virtual void Index(const QAtomicInt* cancelled);
protected:
bool open_;
+57 -24
View File
@@ -192,7 +192,7 @@ bool FFmpegDecoder::Open()
return true;
}
FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode)
FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const QAtomicInt* cancelled)
{
if (!open_ && !Open()) {
return nullptr;
@@ -203,7 +203,7 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode)
}
// Convert timecode to AVStream timebase
int64_t target_ts = GetTimestampFromTime(timecode);
int64_t target_ts = GetTimestampFromTime(timecode, cancelled);
if (target_ts < 0) {
Error(QStringLiteral("Index failed to produce a valid timestamp"));
@@ -318,7 +318,7 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode)
return nullptr;
}
FramePtr FFmpegDecoder::RetrieveAudio(const rational &timecode, const rational &length, const AudioRenderingParams &params)
FramePtr FFmpegDecoder::RetrieveAudio(const rational &timecode, const rational &length, const AudioRenderingParams &params, const QAtomicInt* cancelled)
{
if (!open_ && !Open()) {
return nullptr;
@@ -328,9 +328,9 @@ FramePtr FFmpegDecoder::RetrieveAudio(const rational &timecode, const rational &
return nullptr;
}
Index();
Index(cancelled);
Conform(params);
Conform(params, cancelled);
WaveInput input(GetConformedFilename(params));
@@ -395,7 +395,7 @@ QString FFmpegDecoder::id()
return "ffmpeg";
}
int64_t FFmpegDecoder::GetTimestampFromTime(const rational &time)
int64_t FFmpegDecoder::GetTimestampFromTime(const rational &time, const QAtomicInt* cancelled)
{
if (!open_ && !Open()) {
return -1;
@@ -408,19 +408,19 @@ int64_t FFmpegDecoder::GetTimestampFromTime(const rational &time)
target_ts += avstream_->start_time;
// Find closest actual timebase in the file
target_ts = GetClosestTimestampInIndex(target_ts);
target_ts = GetClosestTimestampInIndex(target_ts, cancelled);
return target_ts;
}
void FFmpegDecoder::Conform(const AudioRenderingParams &params)
void FFmpegDecoder::Conform(const AudioRenderingParams &params, const QAtomicInt* cancelled)
{
if (avstream_->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
// Nothing to be done
return;
}
Index();
Index(cancelled);
// Get indexed WAV file
WaveInput input(GetIndexFilename());
@@ -470,6 +470,10 @@ void FFmpegDecoder::Conform(const AudioRenderingParams &params)
int input_buffer_sz = input.params().time_to_bytes(1);
while (!input.at_end()) {
if (cancelled && *cancelled) {
break;
}
// Read up to one second of audio from WAV file
QByteArray read_samples = input.read(input_buffer_sz);
@@ -486,6 +490,11 @@ void FFmpegDecoder::Conform(const AudioRenderingParams &params)
swr_free(&resampler);
conformed_output.close();
input.close();
// If we cancelled, the conform didn't finish so remove it
if (cancelled && *cancelled) {
QFile(conformed_fn).remove();
}
} else {
qWarning() << "Failed to conform file:" << stream()->footage()->filename();
}
@@ -531,7 +540,7 @@ void FFmpegDecoder::ConformInternal(SwrContext* resampler, WaveOutput* output, c
output->write(out_samples);
}
bool FFmpegDecoder::Probe(Footage *f)
bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
{
if (open_) {
qWarning() << "Probe must be called while the Decoder is closed";
@@ -648,7 +657,7 @@ bool FFmpegDecoder::Probe(Footage *f)
Open();
// Use index to find duration
Index();
Index(cancelled);
// Use last frame index as the duration
// FIXME: Does this skip the last frame?
@@ -688,7 +697,7 @@ void FFmpegDecoder::Error(const QString &s)
Close();
}
void FFmpegDecoder::Index()
void FFmpegDecoder::Index(const QAtomicInt* cancelled)
{
if (!open_) {
qWarning() << "Indexing function tried to run while decoder was closed";
@@ -699,11 +708,11 @@ void FFmpegDecoder::Index()
if (stream()->type() == Stream::kVideo) {
ValidateVideoIndex();
ValidateVideoIndex(cancelled);
} else if (stream()->type() == Stream::kAudio) {
if (!QFileInfo::exists(GetIndexFilename())) {
UnconditionalAudioIndex(pkt_, frame_);
UnconditionalAudioIndex(pkt_, frame_, cancelled);
}
}
}
@@ -746,7 +755,7 @@ QString FFmpegDecoder::GetConformedFilename(const AudioRenderingParams &params)
return index_fn;
}
void FFmpegDecoder::UnconditionalAudioIndex(AVPacket *pkt, AVFrame *frame)
void FFmpegDecoder::UnconditionalAudioIndex(AVPacket *pkt, AVFrame *frame, const QAtomicInt* cancelled)
{
// Iterate through each audio frame and extract the PCM data
@@ -792,6 +801,11 @@ void FFmpegDecoder::UnconditionalAudioIndex(AVPacket *pkt, AVFrame *frame)
if (wave_out.open()) {
while (true) {
// Check if we have a `cancelled` ptr and its value
if (cancelled && *cancelled) {
break;
}
ret = GetFrame(pkt, frame);
if (ret < 0) {
@@ -837,6 +851,11 @@ void FFmpegDecoder::UnconditionalAudioIndex(AVPacket *pkt, AVFrame *frame)
}
wave_out.close();
if (cancelled && *cancelled) {
// Audio index didn't complete, delete it
QFile(GetIndexFilename()).remove();
}
} else {
qWarning() << "Failed to open WAVE output for indexing";
}
@@ -848,7 +867,7 @@ void FFmpegDecoder::UnconditionalAudioIndex(AVPacket *pkt, AVFrame *frame)
Seek(0);
}
void FFmpegDecoder::UnconditionalVideoIndex(AVPacket* pkt, AVFrame* frame)
void FFmpegDecoder::UnconditionalVideoIndex(AVPacket* pkt, AVFrame* frame, const QAtomicInt* cancelled)
{
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
@@ -863,6 +882,11 @@ void FFmpegDecoder::UnconditionalVideoIndex(AVPacket* pkt, AVFrame* frame)
int ret;
while (true) {
// Check if we have a `cancelled` ptr and its value
if (cancelled && *cancelled) {
break;
}
ret = GetFrame(pkt, frame);
if (ret >= 0) {
@@ -875,11 +899,16 @@ void FFmpegDecoder::UnconditionalVideoIndex(AVPacket* pkt, AVFrame* frame)
}
}
video_stream->append_frame_index(VideoStream::kEndTimestamp);
// Check if we have a `cancelled` ptr and its value
if (cancelled && *cancelled) {
video_stream->clear_frame_index();
} else {
video_stream->append_frame_index(VideoStream::kEndTimestamp);
// Save index to file
if (!video_stream->save_frame_index(GetIndexFilename())) {
qWarning() << QStringLiteral("Failed to save index for %1").arg(stream()->footage()->filename());
// Save index to file
if (!video_stream->save_frame_index(GetIndexFilename())) {
qWarning() << QStringLiteral("Failed to save index for %1").arg(stream()->footage()->filename());
}
}
Seek(0);
@@ -930,7 +959,7 @@ int FFmpegDecoder::GetFrame(AVPacket *pkt, AVFrame *frame)
return ret;
}
int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts)
int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts, const QAtomicInt* cancelled)
{
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
@@ -942,7 +971,7 @@ int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts)
// If not, check if the frame index has been populated
if (!video_stream->is_frame_index_ready()) {
// If not, make a frame index
ValidateVideoIndex();
ValidateVideoIndex(cancelled);
}
video_stream->index_process_lock()->unlock();
@@ -954,6 +983,10 @@ int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts)
int64_t closest_ts = -1;
do {
if (cancelled && *cancelled) {
return -1;
}
if (index_is_being_created && video_stream->index_process_lock()->tryLock()) {
index_is_being_created = false;
video_stream->index_process_lock()->unlock();
@@ -969,7 +1002,7 @@ int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts)
return closest_ts;
}
void FFmpegDecoder::ValidateVideoIndex()
void FFmpegDecoder::ValidateVideoIndex(const QAtomicInt* cancelled)
{
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
@@ -981,7 +1014,7 @@ void FFmpegDecoder::ValidateVideoIndex()
// Reset state
Seek(0);
UnconditionalVideoIndex(pkt_, frame_);
UnconditionalVideoIndex(pkt_, frame_, cancelled);
Seek(0);
}
+10 -10
View File
@@ -47,25 +47,25 @@ public:
// Destructor
virtual ~FFmpegDecoder() override;
virtual bool Probe(Footage *f) override;
virtual bool Probe(Footage *f, const QAtomicInt *cancelled) override;
virtual bool Open() override;
virtual FramePtr RetrieveVideo(const rational &timecode) override;
virtual FramePtr RetrieveAudio(const rational &timecode, const rational &length, const AudioRenderingParams& params) override;
virtual FramePtr RetrieveVideo(const rational &timecode, const QAtomicInt *cancelled) override;
virtual FramePtr RetrieveAudio(const rational &timecode, const rational &length, const AudioRenderingParams& params, const QAtomicInt *cancelled) override;
virtual void Close() override;
virtual QString id() override;
virtual int64_t GetTimestampFromTime(const rational& time) override;
virtual int64_t GetTimestampFromTime(const rational& time, const QAtomicInt *cancelled) override;
virtual void Conform(const AudioRenderingParams& params) override;
virtual void Conform(const AudioRenderingParams& params, const QAtomicInt *cancelled) override;
virtual bool SupportsVideo() override;
virtual bool SupportsAudio() override;
void SetMultithreading(bool e);
virtual void Index() override;
virtual void Index(const QAtomicInt *cancelled) override;
private:
void ConformInternal(SwrContext *resampler, WaveOutput *output, const char *in_data, int in_sample_count);
@@ -110,12 +110,12 @@ private:
*/
QString GetConformedFilename(const AudioRenderingParams &params);
void UnconditionalAudioIndex(AVPacket* pkt, AVFrame* frame);
void UnconditionalVideoIndex(AVPacket* pkt, AVFrame* frame);
void UnconditionalAudioIndex(AVPacket* pkt, AVFrame* frame, const QAtomicInt* cancelled);
void UnconditionalVideoIndex(AVPacket* pkt, AVFrame* frame, const QAtomicInt* cancelled);
int64_t GetClosestTimestampInIndex(const int64_t& ts);
int64_t GetClosestTimestampInIndex(const int64_t& ts, const QAtomicInt *cancelled);
void ValidateVideoIndex();
void ValidateVideoIndex(const QAtomicInt* cancelled);
void Seek(int64_t timestamp);
+3 -3
View File
@@ -38,7 +38,7 @@ QString OIIODecoder::id()
return "oiio";
}
bool OIIODecoder::Probe(Footage *f)
bool OIIODecoder::Probe(Footage *f, const QAtomicInt *cancelled)
{
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
@@ -133,7 +133,7 @@ bool OIIODecoder::Open()
return true;
}
FramePtr OIIODecoder::RetrieveVideo(const rational &timecode)
FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const QAtomicInt *cancelled)
{
if (!open_ && !Open()) {
return nullptr;
@@ -174,7 +174,7 @@ void OIIODecoder::Close()
frame_ = nullptr;
}
int64_t OIIODecoder::GetTimestampFromTime(const rational &time)
int64_t OIIODecoder::GetTimestampFromTime(const rational &time, const QAtomicInt *cancelled)
{
Q_UNUSED(time)
+3 -3
View File
@@ -33,15 +33,15 @@ public:
virtual QString id() override;
virtual bool Probe(Footage *f) override;
virtual bool Probe(Footage *f, const QAtomicInt* cancelled) override;
virtual bool Open() override;
virtual FramePtr RetrieveVideo(const rational &timecode) override;
virtual FramePtr RetrieveVideo(const rational &timecode, const QAtomicInt* cancelled) override;
virtual void Close() override;
virtual int64_t GetTimestampFromTime(const rational &time) override;
virtual int64_t GetTimestampFromTime(const rational &time, const QAtomicInt* cancelled) override;
virtual bool SupportsVideo() override;