]> git.sesse.net Git - nageru/blobdiff - nageru/ffmpeg_capture.h
Export SRT statistics as Prometheus metrics.
[nageru] / nageru / ffmpeg_capture.h
index 8a513df971fa20f2cba71e25ac603242b9be3be3..4cddbfddf757bce77a1f49b6c21b52bd7b57675b 100644 (file)
 // changes parameters midway, which is allowed in some formats.
 //
 // You can get out the audio either as decoded or in raw form (Kaeru uses this).
+//
+// If there's a subtitle track, you can also get out the last subtitle at the
+// point of the frame. Note that once we get a video frame, we don't look for
+// subtitle, so if subtitles and a frame comes at the same time, you might not
+// see the subtitle until the next frame.
 
 #include <assert.h>
 #include <stdint.h>
 #include <string>
 #include <thread>
 
+#include <movit/effect.h>
 #include <movit/ycbcr.h>
 
 extern "C" {
-#include <libavresample/avresample.h>
+#include <libswresample/swresample.h>
 #include <libavutil/pixfmt.h>
 #include <libavutil/rational.h>
 #include <libavutil/samplefmt.h>
 }
 
 #include "bmusb/bmusb.h"
-#include "ffmpeg_raii.h"
+#include "shared/ffmpeg_raii.h"
 #include "ref_counted_frame.h"
 #include "quittable_sleeper.h"
 
@@ -51,6 +57,10 @@ class FFmpegCapture : public bmusb::CaptureInterface
 {
 public:
        FFmpegCapture(const std::string &filename, unsigned width, unsigned height);
+#ifdef HAVE_SRT
+       // Takes ownership of the SRT client socket.
+       FFmpegCapture(int srt_sock, const std::string &stream_id);
+#endif
        ~FFmpegCapture();
 
        void set_card_index(int card_index)
@@ -164,6 +174,24 @@ public:
                return current_frame_ycbcr_format;
        }
 
+       // Only valid to call during the frame callback.
+       std::string get_last_subtitle() const
+       {
+               return last_subtitle;
+       }
+
+       // Same.
+       bool get_has_last_subtitle() const
+       {
+               return has_last_subtitle;
+       }
+
+       // Same.
+       movit::RGBTriplet get_last_neutral_color() const
+       {
+               return last_neutral_color;
+       }
+
        void set_dequeue_thread_callbacks(std::function<void()> init, std::function<void()> cleanup) override
        {
                dequeue_init_callback = init;
@@ -171,6 +199,11 @@ public:
                has_dequeue_callbacks = true;
        }
 
+       void set_card_disconnected_callback(bmusb::card_disconnected_callback_t callback)
+       {
+               card_disconnected_callback = callback;
+       }
+
        std::string get_description() const override
        {
                return description;
@@ -179,7 +212,8 @@ public:
        void configure_card() override;
        void start_bm_capture() override;
        void stop_dequeue_thread() override;
-       bool get_disconnected() const override { return false; }  // We never unplug.
+       bool get_disconnected() const override { return disconnected; }  // Only if play_once == true.
+       int get_srt_sock() const { return srt_sock; }
 
        std::map<uint32_t, bmusb::VideoMode> get_available_video_modes() const override;
        void set_video_mode(uint32_t video_mode_id) override {}  // Ignore.
@@ -218,25 +252,35 @@ private:
 
        // Returns nullptr if no frame was decoded (e.g. EOF).
        AVFrameWithDeleter decode_frame(AVFormatContext *format_ctx, AVCodecContext *video_codec_ctx, AVCodecContext *audio_codec_ctx,
-                                       const std::string &pathname, int video_stream_index, int audio_stream_index,
+                                       const std::string &pathname, int video_stream_index, int audio_stream_index, int subtitle_stream_index,
                                        bmusb::FrameAllocator::Frame *audio_frame, bmusb::AudioFormat *audio_format, int64_t *audio_pts, bool *error);
        void convert_audio(const AVFrame *audio_avframe, bmusb::FrameAllocator::Frame *audio_frame, bmusb::AudioFormat *audio_format);
 
        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);
+       static int interrupt_cb_thunk(void *opaque);
        int interrupt_cb();
 
+#ifdef HAVE_SRT
+       static int read_srt_thunk(void *opaque, uint8_t *buf, int buf_size);
+       int read_srt(uint8_t *buf, int buf_size);
+#endif
+
+       inline unsigned frame_width(const AVFrame *frame) const;
+       inline unsigned frame_height(const AVFrame *frame) const;
+
        mutable std::mutex filename_mu;
        std::string description, filename;
+       int srt_sock = -1;
        uint16_t timecode = 0;
-       unsigned width, height;
+       unsigned width, height;  // 0 means keep input size.
        bmusb::PixelFormat pixel_format = bmusb::PixelFormat_8BitBGRA;
        movit::YCbCrFormat current_frame_ycbcr_format;
        bool running = false;
        int card_index = -1;
        double rate = 1.0;
+       bool play_as_fast_as_possible = false;  // Activated iff rate >= 10.0.
        std::atomic<bool> should_interrupt{false};
        bool last_frame_was_connected = true;
 
@@ -244,6 +288,10 @@ private:
        std::function<void()> dequeue_init_callback = nullptr;
        std::function<void()> dequeue_cleanup_callback = nullptr;
 
+       bmusb::card_disconnected_callback_t card_disconnected_callback = nullptr;
+       bool play_once = false;  // End thread after playing. Only for SRT, since the socket is ephemeral.
+       std::atomic<bool> disconnected{false};
+
        bmusb::FrameAllocator *video_frame_allocator = nullptr;
        bmusb::FrameAllocator *audio_frame_allocator = nullptr;
        std::unique_ptr<bmusb::FrameAllocator> owned_video_frame_allocator;
@@ -255,6 +303,7 @@ private:
        int sws_last_width = -1, sws_last_height = -1, sws_last_src_format = -1;
        AVPixelFormat sws_dst_format = AVPixelFormat(-1);  // In practice, always initialized.
        AVRational video_timebase, audio_timebase;
+       bool is_mjpeg = false;
 
        QuittableSleeper producer_thread_should_quit;
        std::thread producer_thread;
@@ -270,11 +319,16 @@ private:
        std::vector<QueuedCommand> command_queue;  // Protected by <queue_mu>.
 
        // Audio resampler.
-       AVAudioResampleContext *resampler = nullptr;
+       SwrContext *resampler = nullptr;
        AVSampleFormat last_src_format, last_dst_format;
        int64_t last_channel_layout;
        int last_sample_rate;
 
+       // Subtitles (no decoding done, really).
+       bool has_last_subtitle = false;
+       std::string last_subtitle;
+
+       movit::RGBTriplet last_neutral_color{1.0f, 1.0f, 1.0f};
 };
 
 #endif  // !defined(_FFMPEG_CAPTURE_H)