From: Steinar H. Gunderson Date: Sat, 14 Apr 2018 21:47:13 +0000 (+0200) Subject: Add a feature on VideoInput to interrupt the playing video. X-Git-Tag: 1.7.2~40 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=6f578d03677866ad1135a21b807ab0167295e38f Add a feature on VideoInput to interrupt the playing video. Useful for when using FFmpeg sources to play network streams, since FFmpeg can sometimes hang forever if the remote server goes away. --- diff --git a/ffmpeg_capture.cpp b/ffmpeg_capture.cpp index bbbb941..50b4fa4 100644 --- a/ffmpeg_capture.cpp +++ b/ffmpeg_capture.cpp @@ -288,6 +288,7 @@ void FFmpegCapture::producer_thread_func() 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()); @@ -352,7 +353,7 @@ bool FFmpegCapture::play_video(const string &pathname) 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; @@ -801,3 +802,13 @@ UniqueFrame FFmpegCapture::make_video_frame(const AVFrame *frame, const string & return video_frame; } + +int FFmpegCapture::interrupt_cb_thunk(void *unique) +{ + return reinterpret_cast(unique)->interrupt_cb(); +} + +int FFmpegCapture::interrupt_cb() +{ + return should_interrupt.load(); +} diff --git a/ffmpeg_capture.h b/ffmpeg_capture.h index f718c70..705d25f 100644 --- a/ffmpeg_capture.h +++ b/ffmpeg_capture.h @@ -78,6 +78,12 @@ public: 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 { @@ -207,6 +213,9 @@ private: 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; @@ -215,6 +224,7 @@ private: bool running = false; int card_index = -1; double rate = 1.0; + std::atomic should_interrupt{false}; bool has_dequeue_callbacks = false; std::function dequeue_init_callback = nullptr; diff --git a/ffmpeg_raii.cpp b/ffmpeg_raii.cpp index a11075f..746e03d 100644 --- a/ffmpeg_raii.cpp +++ b/ffmpeg_raii.cpp @@ -18,16 +18,25 @@ void avformat_close_input_unique::operator() (AVFormatContext *format_ctx) const } 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 diff --git a/ffmpeg_raii.h b/ffmpeg_raii.h index c0c4a47..33d2334 100644 --- a/ffmpeg_raii.h +++ b/ffmpeg_raii.h @@ -19,6 +19,7 @@ struct AVFormatContext; struct AVFrame; struct AVInputFormat; struct SwsContext; +typedef struct AVIOInterruptCB AVIOInterruptCB; // AVFormatContext struct avformat_close_input_unique { @@ -29,7 +30,13 @@ typedef std::unique_ptr 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 diff --git a/theme.cpp b/theme.cpp index 32a00ff..45df66f 100644 --- a/theme.cpp +++ b/theme.cpp @@ -463,6 +463,14 @@ int VideoInput_rewind(lua_State* L) 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); @@ -759,6 +767,7 @@ const luaL_Reg ImageInput_funcs[] = { 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 }