#include "extractor.h"
#include "general/thread.h"
#include "general/scope.h"
#include "general/exception.h"
#include <cfloat>
using namespace std;
#ifdef USE_PYBIND11
#include <pybind11/pybind11.h>
#endif
Extractor::Extractor(shared_ptr<Demux> demux) :
m_state(State::idle),
m_demux(demux),
m_beginTime(0.0),
m_endTime(DBL_MAX)
{}
Extractor::~Extractor()
{
stop();
wait();
}
const StreamsFormat &Extractor::getStreamsInfo() const
{
return m_demux->getStreamsInfo();
}
void Extractor::selectTimeWindow(double begin, double end)
{
if (begin > end || begin != begin || end != end)
throw EXCEPTION("invalid time window").module("extractor")
.add("begin", begin).add("end", end);
m_beginTime = begin;
m_endTime = end;
}
void Extractor::selectEndTime(double end)
{
m_endTime = end;
}
void Extractor::connectEosCallback(EosCallback callback)
{
m_eosCb = callback;
}
void Extractor::connectErrorCallback(ErrorCallback callback)
{
m_errorCb = callback;
}
void Extractor::start(const string &threadName)
{
m_state = State::running;
m_thread = thread(&Extractor::run, this, threadName);
}
void Extractor::stop()
{
if (m_state == State::running)
m_state = State::stopping;
}
bool Extractor::isRunning() const
{
return m_state != State::idle;
}
void Extractor::run(string threadName)
{
ScopeExit done([this](){ m_state = State::idle; });
lowerThreadPriority();
if (!threadName.empty())
renameThread(threadName);
Demux *demux = m_demux.get();
try
{
if (m_beginTime)
demux->seek(m_beginTime);
demux->start();
while (m_state == State::running)
{
try
{
while (m_state == State::running)
{
if (!demux->step() || (demux->getPosition() >= m_endTime))
m_state = State::stopping;
}
}
catch (const Exception &ex)
{
if (m_state == State::running)
demux->notifyDiscontinuity();
if (m_errorCb && ex.get("averror") != "AVERROR_EXIT")
m_errorCb(ex);
}
}
}
catch (const Exception &ex)
{
if (m_errorCb && ex.get("averror") != "AVERROR_EXIT")
m_errorCb(ex);
}
catch (const std::exception& ex)
{
if (m_errorCb)
m_errorCb(EXCEPTION(ex.what()).module("extractor"));
}
catch (...)
{
if (m_errorCb)
m_errorCb(EXCEPTION("fatal error").module("extractor"));
}
try
{
demux->stop();
}
catch (...)
{ }
m_demux = NULL;
m_state = State::idle;
try
{
if (m_eosCb)
m_eosCb();
}
catch (...)
{ }
}
void Extractor::wait()
{
if (m_thread.joinable())
{
#ifdef USE_PYBIND11
pybind11::gil_scoped_release release;
#endif
m_thread.join();
}
m_state = State::idle;
}