X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=mixer.h;h=8504d5eb4bb9d6a56d8b68dd2ab04464b0bcecfd;hb=96cb6414f85e0ef4d660b7bd56267303e80fcd05;hp=191ea3dfe7b0835fecf011dcfac96c9e3cb2165d;hpb=9821f303f6d295ae85521aaa83c275c6305b644c;p=nageru diff --git a/mixer.h b/mixer.h index 191ea3d..8504d5e 100644 --- a/mixer.h +++ b/mixer.h @@ -23,6 +23,8 @@ #include #include +#include + #include "audio_mixer.h" #include "bmusb/bmusb.h" #include "defs.h" @@ -35,18 +37,21 @@ #include "theme.h" #include "timebase.h" #include "video_encoder.h" +#include "ycbcr_interpretation.h" class ALSAOutput; class ChromaSubsampler; class DeckLinkOutput; class QSurface; class QSurfaceFormat; +class TimecodeRenderer; +class v210Converter; namespace movit { class Effect; class EffectChain; -class FlatInput; class ResourcePool; +class YCbCrInput; } // namespace movit // For any card that's not the master (where we pick out the frames as they @@ -83,6 +88,8 @@ public: been_at_safe_point_since_last_starvation = false; } + void register_metrics(const std::vector> &labels); + void update_policy(unsigned queue_length); // Call before picking out a frame, so 0 means starvation. unsigned get_safe_queue_length() const { return safe_queue_length; } @@ -91,6 +98,11 @@ private: unsigned safe_queue_length = 1; // Called N in the comments. Can never go below 1. unsigned frames_with_at_least_one = 0; bool been_at_safe_point_since_last_starvation = false; + + // Metrics. + std::atomic metric_input_queue_length_frames{0}; + std::atomic metric_input_queue_safe_length_frames{1}; + std::atomic metric_input_duped_frames{0}; }; class Mixer { @@ -137,10 +149,17 @@ public: return output_channel[output].get_display_frame(frame); } + // NOTE: Callbacks will be called with a mutex held, so you should probably + // not do real work in them. typedef std::function new_frame_ready_callback_t; - void set_frame_ready_callback(Output output, new_frame_ready_callback_t callback) + void add_frame_ready_callback(Output output, void *key, new_frame_ready_callback_t callback) + { + output_channel[output].add_frame_ready_callback(key, callback); + } + + void remove_frame_ready_callback(Output output, void *key) { - output_channel[output].set_frame_ready_callback(callback); + output_channel[output].remove_frame_ready_callback(key); } // TODO: Should this really be per-channel? Shouldn't it just be called for e.g. the live output? @@ -207,6 +226,9 @@ public: return theme->set_signal_mapping(signal, card); } + YCbCrInterpretation get_input_ycbcr_interpretation(unsigned card_index) const; + void set_input_ycbcr_interpretation(unsigned card_index, const YCbCrInterpretation &interpretation); + bool get_supports_set_wb(unsigned channel) const { return theme->get_supports_set_wb(channel); @@ -321,10 +343,23 @@ public: desired_output_video_mode = mode; } + void set_display_timecode_in_stream(bool enable) { + display_timecode_in_stream = enable; + } + + void set_display_timecode_on_stdout(bool enable) { + display_timecode_on_stdout = enable; + } + private: struct CaptureCard; - void configure_card(unsigned card_index, bmusb::CaptureInterface *capture, bool is_fake_capture, DeckLinkOutput *output); + enum class CardType { + LIVE_CARD, + FAKE_CAPTURE, + FFMPEG_INPUT + }; + void configure_card(unsigned card_index, bmusb::CaptureInterface *capture, CardType card_type, DeckLinkOutput *output); void set_output_card_internal(int card_index); // Should only be called from the mixer thread. void bm_frame(unsigned card_index, uint16_t timecode, bmusb::FrameAllocator::Frame video_frame, size_t video_offset, bmusb::VideoFormat video_format, @@ -335,6 +370,7 @@ private: void thread_func(); void handle_hotplugged_cards(); void schedule_audio_resampling_tasks(unsigned dropped_frames, int num_samples_per_frame, int length_per_frame, bool is_preroll, std::chrono::steady_clock::time_point frame_timestamp); + std::string get_timecode_text() const; void render_one_frame(int64_t duration); void audio_thread_func(); void release_display_frame(DisplayFrame *frame); @@ -344,7 +380,7 @@ private: void trim_queue(CaptureCard *card, unsigned card_index); HTTPD httpd; - unsigned num_cards; + unsigned num_cards, num_video_inputs; QSurface *mixer_surface, *h264_encoder_surface, *decklink_output_surface; std::unique_ptr resource_pool; @@ -364,12 +400,18 @@ private: std::unique_ptr display_chain; std::unique_ptr chroma_subsampler; + std::unique_ptr v210_converter; std::unique_ptr video_encoder; + std::unique_ptr timecode_renderer; + std::atomic display_timecode_in_stream{false}; + std::atomic display_timecode_on_stdout{false}; + // Effects part of . Owned by . - movit::FlatInput *display_input; + movit::YCbCrInput *display_input; int64_t pts_int = 0; // In TIMEBASE units. + unsigned frame_num = 0; // Accumulated errors in number of 1/TIMEBASE audio samples. If OUTPUT_FREQUENCY divided by // frame rate is integer, will always stay zero. @@ -380,6 +422,7 @@ private: struct CaptureCard { std::unique_ptr capture; bool is_fake_capture; + CardType type; std::unique_ptr output; // If this card is used for output (ie., output_card_index points to it), @@ -410,8 +453,23 @@ private: QueueLengthPolicy queue_length_policy; // Refers to the "new_frames" queue. int last_timecode = -1; // Unwrapped. + + // Metrics. + std::atomic metric_input_dropped_frames_jitter{0}; + std::atomic metric_input_dropped_frames_error{0}; + std::atomic metric_input_resets{0}; + + std::atomic metric_input_has_signal_bool{-1}; + std::atomic metric_input_is_connected_bool{-1}; + std::atomic metric_input_interlaced_bool{-1}; + std::atomic metric_input_width_pixels{-1}; + std::atomic metric_input_height_pixels{-1}; + std::atomic metric_input_frame_rate_nom{-1}; + std::atomic metric_input_frame_rate_den{-1}; + std::atomic metric_input_sample_rate_hz{-1}; }; CaptureCard cards[MAX_VIDEO_CARDS]; // Protected by . + YCbCrInterpretation ycbcr_interpretation[MAX_VIDEO_CARDS]; // Protected by . AudioMixer audio_mixer; // Same as global_audio_mixer (see audio_mixer.h). bool input_card_is_master_clock(unsigned card_index, unsigned master_card_index) const; struct OutputFrameInfo { @@ -435,7 +493,8 @@ private: ~OutputChannel(); void output_frame(DisplayFrame frame); bool get_display_frame(DisplayFrame *frame); - void set_frame_ready_callback(new_frame_ready_callback_t callback); + void add_frame_ready_callback(void *key, new_frame_ready_callback_t callback); + void remove_frame_ready_callback(void *key); void set_transition_names_updated_callback(transition_names_updated_callback_t callback); void set_name_updated_callback(name_updated_callback_t callback); void set_color_updated_callback(color_updated_callback_t callback); @@ -448,7 +507,7 @@ private: std::mutex frame_mutex; DisplayFrame current_frame, ready_frame; // protected by bool has_current_frame = false, has_ready_frame = false; // protected by - new_frame_ready_callback_t new_frame_ready_callback; + std::map new_frame_ready_callbacks; // protected by transition_names_updated_callback_t transition_names_updated_callback; name_updated_callback_t name_updated_callback; color_updated_callback_t color_updated_callback; @@ -480,6 +539,13 @@ private: std::vector mode_scanlist[MAX_VIDEO_CARDS]; unsigned mode_scanlist_index[MAX_VIDEO_CARDS]{ 0 }; std::chrono::steady_clock::time_point last_mode_scan_change[MAX_VIDEO_CARDS]; + + // Metrics. + std::atomic metric_frames_output_total{0}; + std::atomic metric_frames_output_dropped{0}; + std::atomic metric_start_time_seconds{0.0 / 0.0}; + std::atomic metrics_memory_used_bytes{0}; + std::atomic metrics_memory_locked_limit_bytes{0.0 / 0.0}; }; extern Mixer *global_mixer;