for (unsigned card_index = 0; card_index < num_cards; ++card_index) {
{
- unique_lock<mutex> lock(bmusb_mutex);
+ unique_lock<mutex> lock(card_mutex);
cards[card_index].should_quit = true; // Unblock thread.
cards[card_index].new_frames_changed.notify_all();
}
// Found a stable signal, so stop scanning.
is_mode_scanning[card_index] = false;
} else {
- static constexpr double switch_time_s = 0.5; // Should be enough time for the signal to stabilize.
+ static constexpr double switch_time_s = 0.1; // Should be enough time for the signal to stabilize.
steady_clock::time_point now = steady_clock::now();
double sec_since_last_switch = duration<double>(steady_clock::now() - last_mode_scan_change[card_index]).count();
if (sec_since_last_switch > switch_time_s) {
// Still send on the information that we _had_ a frame, even though it's corrupted,
// so that pts can go up accordingly.
{
- unique_lock<mutex> lock(bmusb_mutex);
+ unique_lock<mutex> lock(card_mutex);
CaptureCard::NewFrame new_frame;
new_frame.frame = RefCountedFrame(FrameAllocator::Frame());
new_frame.length = frame_length;
unsigned num_fields = video_format.interlaced ? 2 : 1;
steady_clock::time_point frame_upload_start;
+ bool interlaced_stride = false;
if (video_format.interlaced) {
// Send the two fields along as separate frames; the other side will need to add
// a deinterlacer to actually get this right.
assert(frame_length % 2 == 0);
frame_length /= 2;
num_fields = 2;
+ if (video_format.second_field_start == 1) {
+ interlaced_stride = true;
+ }
frame_upload_start = steady_clock::now();
}
userdata->last_interlaced = video_format.interlaced;
// Note that this means we must hold on to the actual frame data in <userdata>
// until the upload command is run, but we hold on to <frame> much longer than that
// (in fact, all the way until we no longer use the texture in rendering).
- auto upload_func = [field, video_format, y_offset, cbcr_offset, cbcr_width, userdata]() {
- unsigned field_start_line = (field == 1) ? video_format.second_field_start : video_format.extra_lines_top + field * (video_format.height + 22);
+ auto upload_func = [field, video_format, y_offset, cbcr_offset, cbcr_width, interlaced_stride, userdata]() {
+ unsigned field_start_line;
+ if (field == 1) {
+ field_start_line = video_format.second_field_start;
+ } else {
+ field_start_line = video_format.extra_lines_top;
+ }
if (userdata->tex_y[field] == 0 ||
userdata->tex_cbcr[field] == 0 ||
glBindTexture(GL_TEXTURE_2D, userdata->tex_cbcr[field]);
check_error();
+ if (interlaced_stride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, cbcr_width * 2);
+ check_error();
+ } else {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ check_error();
+ }
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, cbcr_width, video_format.height, GL_RG, GL_UNSIGNED_BYTE, BUFFER_OFFSET(field_cbcr_start));
check_error();
glBindTexture(GL_TEXTURE_2D, userdata->tex_y[field]);
check_error();
+ if (interlaced_stride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, video_format.width * 2);
+ check_error();
+ } else {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ check_error();
+ }
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_format.width, video_format.height, GL_RED, GL_UNSIGNED_BYTE, BUFFER_OFFSET(field_y_start));
check_error();
glBindTexture(GL_TEXTURE_2D, 0);
check_error();
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
check_error();
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ check_error();
};
if (field == 1) {
}
{
- unique_lock<mutex> lock(bmusb_mutex);
+ unique_lock<mutex> lock(card_mutex);
CaptureCard::NewFrame new_frame;
new_frame.frame = frame;
new_frame.length = frame_length;
start:
// The first card is the master timer, so wait for it to have a new frame.
// TODO: Add a timeout.
- unique_lock<mutex> lock(bmusb_mutex);
+ unique_lock<mutex> lock(card_mutex);
cards[master_card_index].new_frames_changed.wait(lock, [this, master_card_index]{ return !cards[master_card_index].new_frames.empty() || cards[master_card_index].capture->get_disconnected(); });
if (cards[master_card_index].new_frames.empty()) {