#include <thread>
#include <vector>
+#include <movit/image_format.h>
+
#include "audio_mixer.h"
#include "bmusb/bmusb.h"
#include "defs.h"
#include "theme.h"
#include "timebase.h"
#include "video_encoder.h"
+#include "ycbcr_interpretation.h"
class ALSAOutput;
class ChromaSubsampler;
class DeckLinkOutput;
-class TimecodeRenderer;
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
been_at_safe_point_since_last_starvation = false;
}
+ void register_metrics(const std::vector<std::pair<std::string, std::string>> &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; }
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<int64_t> metric_input_queue_length_frames{0};
+ std::atomic<int64_t> metric_input_queue_safe_length_frames{1};
+ std::atomic<int64_t> metric_input_duped_frames{0};
};
class Mixer {
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<void()> 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?
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);
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,
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<movit::ResourcePool> resource_pool;
std::unique_ptr<movit::EffectChain> display_chain;
std::unique_ptr<ChromaSubsampler> chroma_subsampler;
+ std::unique_ptr<v210Converter> v210_converter;
std::unique_ptr<VideoEncoder> video_encoder;
std::unique_ptr<TimecodeRenderer> timecode_renderer;
std::atomic<bool> display_timecode_on_stdout{false};
// Effects part of <display_chain>. Owned by <display_chain>.
- movit::FlatInput *display_input;
+ movit::YCbCrInput *display_input;
int64_t pts_int = 0; // In TIMEBASE units.
unsigned frame_num = 0;
struct CaptureCard {
std::unique_ptr<bmusb::CaptureInterface> capture;
bool is_fake_capture;
+ CardType type;
std::unique_ptr<DeckLinkOutput> output;
// If this card is used for output (ie., output_card_index points to it),
QueueLengthPolicy queue_length_policy; // Refers to the "new_frames" queue.
int last_timecode = -1; // Unwrapped.
+
+ // Metrics.
+ std::atomic<int64_t> metric_input_received_frames{0};
+ std::atomic<int64_t> metric_input_dropped_frames_jitter{0};
+ std::atomic<int64_t> metric_input_dropped_frames_error{0};
+ std::atomic<int64_t> metric_input_resets{0};
+
+ std::atomic<int64_t> metric_input_has_signal_bool{-1};
+ std::atomic<int64_t> metric_input_is_connected_bool{-1};
+ std::atomic<int64_t> metric_input_interlaced_bool{-1};
+ std::atomic<int64_t> metric_input_width_pixels{-1};
+ std::atomic<int64_t> metric_input_height_pixels{-1};
+ std::atomic<int64_t> metric_input_frame_rate_nom{-1};
+ std::atomic<int64_t> metric_input_frame_rate_den{-1};
+ std::atomic<int64_t> metric_input_sample_rate_hz{-1};
};
CaptureCard cards[MAX_VIDEO_CARDS]; // Protected by <card_mutex>.
+ YCbCrInterpretation ycbcr_interpretation[MAX_VIDEO_CARDS]; // Protected by <card_mutex>.
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 {
~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);
std::mutex frame_mutex;
DisplayFrame current_frame, ready_frame; // protected by <frame_mutex>
bool has_current_frame = false, has_ready_frame = false; // protected by <frame_mutex>
- new_frame_ready_callback_t new_frame_ready_callback;
+ std::map<void *, new_frame_ready_callback_t> new_frame_ready_callbacks; // protected by <frame_mutex>
transition_names_updated_callback_t transition_names_updated_callback;
name_updated_callback_t name_updated_callback;
color_updated_callback_t color_updated_callback;
std::vector<uint32_t> 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<int64_t> metric_frames_output_total{0};
+ std::atomic<int64_t> metric_frames_output_dropped{0};
+ std::atomic<double> metric_start_time_seconds{0.0 / 0.0};
+ std::atomic<int64_t> metrics_memory_used_bytes{0};
+ std::atomic<double> metrics_memory_locked_limit_bytes{0.0 / 0.0};
};
extern Mixer *global_mixer;