producer_thread_should_quit.sleep_for(seconds(1));
continue;
}
+ should_interrupt = false;
if (!play_video(pathname)) {
// Error.
fprintf(stderr, "Error when playing %s, sleeping one second and trying again...\n", pathname.c_str());
AVDictionary *opts = nullptr;
av_dict_set(&opts, "fflags", "nobuffer", 0);
- auto format_ctx = avformat_open_input_unique(pathname.c_str(), nullptr, &opts);
+ auto format_ctx = avformat_open_input_unique(pathname.c_str(), nullptr, &opts, AVIOInterruptCB{ &FFmpegCapture::interrupt_cb_thunk, this });
if (format_ctx == nullptr) {
fprintf(stderr, "%s: Error opening file\n", pathname.c_str());
return false;
return video_frame;
}
+
+int FFmpegCapture::interrupt_cb_thunk(void *unique)
+{
+ return reinterpret_cast<FFmpegCapture *>(unique)->interrupt_cb();
+}
+
+int FFmpegCapture::interrupt_cb()
+{
+ return should_interrupt.load();
+}
producer_thread_should_quit.wakeup();
}
+ // Will stop the stream even if it's hung on blocking I/O.
+ void disconnect()
+ {
+ should_interrupt = true;
+ }
+
// CaptureInterface.
void set_video_frame_allocator(bmusb::FrameAllocator *allocator) override
{
bmusb::VideoFormat construct_video_format(const AVFrame *frame, AVRational video_timebase);
UniqueFrame make_video_frame(const AVFrame *frame, const std::string &pathname, bool *error);
+ static int interrupt_cb_thunk(void *unique);
+ int interrupt_cb();
+
std::string description, filename;
uint16_t timecode = 0;
unsigned width, height;
bool running = false;
int card_index = -1;
double rate = 1.0;
+ std::atomic<bool> should_interrupt{false};
bool has_dequeue_callbacks = false;
std::function<void()> dequeue_init_callback = nullptr;
}
AVFormatContextWithCloser avformat_open_input_unique(
- const char *pathname, AVInputFormat *fmt, AVDictionary **options)
+ const char *pathname, AVInputFormat *fmt,
+ AVDictionary **options)
{
- AVFormatContext *format_ctx = nullptr;
+ return avformat_open_input_unique(pathname, fmt, options, AVIOInterruptCB{ nullptr, nullptr });
+}
+
+AVFormatContextWithCloser avformat_open_input_unique(
+ const char *pathname, AVInputFormat *fmt,
+ AVDictionary **options,
+ const AVIOInterruptCB &interrupt_cb)
+{
+ AVFormatContext *format_ctx = avformat_alloc_context();
+ format_ctx->interrupt_callback = interrupt_cb;
if (avformat_open_input(&format_ctx, pathname, fmt, options) != 0) {
format_ctx = nullptr;
}
return AVFormatContextWithCloser(format_ctx);
}
-
// AVCodecContext
void avcodec_free_context_unique::operator() (AVCodecContext *codec_ctx) const
struct AVFrame;
struct AVInputFormat;
struct SwsContext;
+typedef struct AVIOInterruptCB AVIOInterruptCB;
// AVFormatContext
struct avformat_close_input_unique {
AVFormatContextWithCloser;
AVFormatContextWithCloser avformat_open_input_unique(
- const char *pathname, AVInputFormat *fmt, AVDictionary **options);
+ const char *pathname, AVInputFormat *fmt,
+ AVDictionary **options);
+
+AVFormatContextWithCloser avformat_open_input_unique(
+ const char *pathname, AVInputFormat *fmt,
+ AVDictionary **options,
+ const AVIOInterruptCB &interrupt_cb);
// AVCodecContext
return 0;
}
+int VideoInput_disconnect(lua_State* L)
+{
+ assert(lua_gettop(L) == 1);
+ FFmpegCapture **video_input = (FFmpegCapture **)luaL_checkudata(L, 1, "VideoInput");
+ (*video_input)->disconnect();
+ return 0;
+}
+
int VideoInput_change_rate(lua_State* L)
{
assert(lua_gettop(L) == 2);
const luaL_Reg VideoInput_funcs[] = {
{ "new", VideoInput_new },
{ "rewind", VideoInput_rewind },
+ { "disconnect", VideoInput_disconnect },
{ "change_rate", VideoInput_change_rate },
{ "get_signal_num", VideoInput_get_signal_num },
{ NULL, NULL }