Skip to content

Commit 11abe21

Browse files
committed
vcap/omt: Move audio recv to own thread
1 parent 2837440 commit 11abe21

1 file changed

Lines changed: 107 additions & 37 deletions

File tree

src/video_capture/omt.cpp

Lines changed: 107 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <algorithm>
3939
#include <cassert>
4040
#include <memory>
41+
#include <thread>
4142

4243
#include "debug.h"
4344
#include "omt_common.hpp"
@@ -49,19 +50,54 @@
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

5557
namespace{
5658
using 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+
5885
struct 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

131157
void 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

178247
video_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

Comments
 (0)