]> git.sesse.net Git - nageru/commitdiff
Add a feature on VideoInput to interrupt the playing video.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 14 Apr 2018 21:47:13 +0000 (23:47 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 14 Apr 2018 21:47:13 +0000 (23:47 +0200)
Useful for when using FFmpeg sources to play network streams,
since FFmpeg can sometimes hang forever if the remote server
goes away.

ffmpeg_capture.cpp
ffmpeg_capture.h
ffmpeg_raii.cpp
ffmpeg_raii.h
theme.cpp

index bbbb941065f90e4a896f65237836b34ffcd4738f..50b4fa45109362d637dfd69c5122725f69e3828d 100644 (file)
@@ -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<FFmpegCapture *>(unique)->interrupt_cb();
+}
+
+int FFmpegCapture::interrupt_cb()
+{
+       return should_interrupt.load();
+}
index f718c70a23ff4dccceb7df40877f389e44815266..705d25ffe5a47461e297401e81e7c6ccd6b69eaf 100644 (file)
@@ -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<bool> should_interrupt{false};
 
        bool has_dequeue_callbacks = false;
        std::function<void()> dequeue_init_callback = nullptr;
index a11075f81d0d8b84e6c17380e15c82683e476d41..746e03d19122da0cd11ad70735670b9b9d318af5 100644 (file)
@@ -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
index c0c4a47dc170aa954862eacaa56eb9326cb739d2..33d233480528dd8414e92b3fd32b04c1281d4ffa 100644 (file)
@@ -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<AVFormatContext, 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
index 32a00ff7ec4ba639939bf34527b414a6ed120255..45df66f85462e7ecdb853c78065de9d72bb3ae2e 100644 (file)
--- 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 }