+ GLuint pbo = userdata->pbo;
+ check_error();
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
+ check_error();
+
+ size_t field_y_start = y_offset + video_format.width * field_start_line;
+ size_t field_cbcr_start = cbcr_offset + cbcr_width * field_start_line * sizeof(uint16_t);
+
+ if (global_flags.flush_pbos) {
+ glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, field_y_start, video_format.width * video_format.height);
+ check_error();
+ glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, field_cbcr_start, cbcr_width * video_format.height * sizeof(uint16_t));
+ check_error();
+ }
+
+ glBindTexture(GL_TEXTURE_2D, userdata->tex_cbcr[field]);
+ 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();
+ 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();
+ };
+
+ if (field == 1) {
+ // Don't upload the second field as fast as we can; wait until
+ // the field time has approximately passed. (Otherwise, we could
+ // get timing jitter against the other sources, and possibly also
+ // against the video display, although the latter is not as critical.)
+ // This requires our system clock to be reasonably close to the
+ // video clock, but that's not an unreasonable assumption.
+ steady_clock::time_point second_field_start = frame_upload_start +
+ nanoseconds(frame_length * 1000000000 / TIMEBASE);
+ this_thread::sleep_until(second_field_start);
+ }
+
+ {
+ unique_lock<mutex> lock(bmusb_mutex);
+ CaptureCard::NewFrame new_frame;
+ new_frame.frame = frame;
+ new_frame.length = frame_length;
+ new_frame.field = field;
+ new_frame.interlaced = video_format.interlaced;
+ new_frame.upload_func = upload_func;
+ new_frame.dropped_frames = dropped_frames;
+ card->new_frames.push(move(new_frame));
+ card->new_frames_changed.notify_all();
+ }