3838#include < algorithm>
3939#include < cassert>
4040#include < memory>
41+ #include < thread>
4142
4243#include " debug.h"
4344#include " omt_common.hpp"
4950#include " audio/utils.h"
5051#include " utils/color_out.h"
5152#include " utils/string_view_utils.hpp"
53+ #include " utils/thread.h"
5254
5355#define MOD_NAME " [omt] "
5456
5557namespace {
5658using video_frame_uniq = std::unique_ptr<video_frame, deleter_from_fcn<vf_free>>;
5759
60+ struct Audio_frame_data {
61+ audio_frame f{};
62+ std::unique_ptr<char []> data;
63+ };
64+
65+ template <typename T>
66+ class Double_buffer {
67+ public:
68+ Double_buffer () = default ;
69+
70+ T& front () noexcept { return bufs[idx]; }
71+ T& back () noexcept { return bufs[idx ^ 1 ]; }
72+
73+ void swap (){
74+ auto lk = lock ();
75+ idx ^= 1 ;
76+ }
77+
78+ [[nodiscard]] std::scoped_lock<std::mutex> lock () { return std::scoped_lock (mutex); }
79+ private:
80+ std::mutex mutex;
81+ T bufs[2 ];
82+ int idx = 0 ;
83+ };
84+
5885struct state_omt_cap {
86+ ~state_omt_cap (){
87+ should_exit = true ;
88+ if (audio_recv_thread.joinable ()){
89+ audio_recv_thread.join ();
90+ }
91+ }
92+
5993 omt_receive_uniq omt_h;
6094
6195 video_frame_uniq ug_frame;
6296
63- audio_frame audio_f{};
64- std::vector<short > ug_audio_f_buf;
97+ Double_buffer<Audio_frame_data> audio_f;
98+
99+ std::atomic<bool > should_exit = false ;
100+ std::thread audio_recv_thread;
65101
66102 std::string sender_addr = " localhost" ;
67103 int port = 6400 ;
@@ -105,53 +141,41 @@ int parse_cfg(state_omt_cap& s, std::string_view cfg){
105141 return VIDCAP_INIT_OK;
106142}
107143
108- int capture_omt_init ( const vidcap_params *params, void **state ){
109- auto s = std::make_unique<state_omt_cap>( );
144+ void init_audio_frame (Audio_frame_data& f, const OMTMediaFrame& omt ){
145+ assert (omt. Type == OMTFrameType_Audio );
110146
111- ug_register_omt_log_callback ();
147+ f.f .ch_count = omt.Channels ;
148+ f.f .bps = 2 ;
149+ f.f .sample_rate = omt.SampleRate ;
112150
113- if (auto parse_ret = parse_cfg (*s, vidcap_params_get_fmt (params)); parse_ret != VIDCAP_INIT_OK){
114- return parse_ret;
115- }
116-
117- std::string addr = " omt://" + s->sender_addr + " :" + std::to_string (s->port );
118-
119- s->omt_h .reset (omt_receive_create (addr.c_str (), static_cast <OMTFrameType>(OMTFrameType_Audio | OMTFrameType_Video),
120- OMTPreferredVideoFormat_UYVY, OMTReceiveFlags_None));
121-
122- *state = s.release ();
123- return VIDCAP_INIT_OK;
124- }
125-
126- void capture_omt_done (void *state){
127- auto s = static_cast <state_omt_cap *>(state);
128- delete s;
151+ f.f .max_size = f.f .ch_count * f.f .bps * f.f .sample_rate ;
152+ f.data .reset (new char [f.f .max_size ]);
153+ f.f .data = f.data .get ();
154+ f.f .data_len = 0 ;
129155}
130156
131157void float2S16 (short *out , const float *in, int samples) {
132- for (int i = 0 ; i < samples; i++) {
158+ for (int i = 0 ; i < samples; i++) {
133159 float sample = in[i];
134160 if (sample < -1 .f ) sample = -1 .f ;
135161 if (sample > 1 .f ) sample = 1 .f ;
136162 out[i] = INT16_MAX * sample;
137163 }
138164}
139165
140- audio_frame * omt_to_audio_frame (state_omt_cap *s , const OMTMediaFrame& omt_audio){
166+ void audio_frame_append (Audio_frame_data& f , const OMTMediaFrame& omt_audio){
141167 constexpr int S16_BPS = 2 ;
142168 const auto ch_count = omt_audio.Channels ;
143169
144- s->ug_audio_f_buf .resize (omt_audio.SamplesPerChannel * ch_count);
145- s->audio_f .ch_count = ch_count;
146- s->audio_f .data = reinterpret_cast <char *>(s->ug_audio_f_buf .data ());
147- s->audio_f .bps = S16_BPS;
148- s->audio_f .data_len = s->ug_audio_f_buf .size () * S16_BPS;
149- s->audio_f .max_size = s->ug_audio_f_buf .size () * S16_BPS;
150- s->audio_f .sample_rate = omt_audio.SampleRate ;
151-
152- int16_t *dst = s->ug_audio_f_buf .data ();
170+ auto dst = reinterpret_cast <int16_t *>(f.f .data + f.f .data_len );
153171 auto src = static_cast <const float *>(omt_audio.Data );
154172 auto samples_left = omt_audio.SamplesPerChannel ;
173+ const auto frame_capacity = (f.f .max_size - f.f .data_len ) / f.f .bps / ch_count;
174+ if (samples_left > frame_capacity){
175+ log_msg (LOG_LEVEL_WARNING, MOD_NAME " Buffer overflow, dropping %d samples\n " , samples_left - frame_capacity);
176+ samples_left = frame_capacity;
177+ }
178+ f.f .data_len += samples_left * f.f .bps * ch_count;
155179 while (samples_left > 0 ){
156180 constexpr auto chunk_samples = 128 ;
157181 const auto to_process = std::min (chunk_samples, samples_left);
@@ -171,17 +195,63 @@ audio_frame *omt_to_audio_frame(state_omt_cap *s, const OMTMediaFrame& omt_audio
171195 src += to_process;
172196 samples_left -= to_process;
173197 }
198+ }
199+
200+ void omt_cap_audio_worker (state_omt_cap *s){
201+ set_thread_name (__func__);
202+
203+ while (!s->should_exit ){
204+ const auto omt_audio = omt_receive (s->omt_h .get (), OMTFrameType_Audio, 10 );
205+ if (!omt_audio)
206+ continue ;
207+
208+ auto lk = s->audio_f .lock ();
209+ auto & f = s->audio_f .back ();
210+
211+ if (!f.data
212+ || f.f .ch_count != omt_audio->Channels
213+ || f.f .sample_rate != omt_audio->SampleRate )
214+ {
215+ init_audio_frame (f, *omt_audio);
216+ }
217+
218+ audio_frame_append (f, *omt_audio);
219+ }
220+ }
221+
222+ int capture_omt_init (const vidcap_params *params, void **state){
223+ auto s = std::make_unique<state_omt_cap>();
224+
225+ ug_register_omt_log_callback ();
226+
227+ if (auto parse_ret = parse_cfg (*s, vidcap_params_get_fmt (params)); parse_ret != VIDCAP_INIT_OK){
228+ return parse_ret;
229+ }
230+
231+ std::string addr = " omt://" + s->sender_addr + " :" + std::to_string (s->port );
232+
233+ s->omt_h .reset (omt_receive_create (addr.c_str (), static_cast <OMTFrameType>(OMTFrameType_Audio | OMTFrameType_Video),
234+ OMTPreferredVideoFormat_UYVY, OMTReceiveFlags_None));
235+
236+ s->audio_recv_thread = std::thread (omt_cap_audio_worker, s.get ());
174237
175- return &s->audio_f ;
238+ *state = s.release ();
239+ return VIDCAP_INIT_OK;
240+ }
241+
242+ void capture_omt_done (void *state){
243+ auto s = static_cast <state_omt_cap *>(state);
244+ delete s;
176245}
177246
178247video_frame *capture_omt_grab (void *state, audio_frame **audio){
179248 auto s = static_cast <state_omt_cap *>(state);
180249
181- const auto omt_audio = omt_receive (s->omt_h .get (), OMTFrameType_Audio, 0 );
182- if (omt_audio){
183- *audio = omt_to_audio_frame (s, *omt_audio);
184- log_msg (LOG_LEVEL_INFO, " got audio %d (%d)\n " , omt_audio->SamplesPerChannel , (*audio)->data_len );
250+ s->audio_f .front ().f .data_len = 0 ;
251+ s->audio_f .swap ();
252+
253+ if (auto & audio_frame = s->audio_f .front (); audio_frame.f .data_len != 0 ){
254+ *audio = &audio_frame.f ;
185255 }
186256
187257 const auto omt_frame = omt_receive (s->omt_h .get (), OMTFrameType_Video, 100 );
0 commit comments