+Mixer::~Mixer()
+{
+ resource_pool->release_glsl_program(cbcr_program_num);
+ BMUSBCapture::stop_bm_thread();
+
+ for (int card_index = 0; card_index < NUM_CARDS; ++card_index) {
+ {
+ unique_lock<mutex> lock(bmusb_mutex);
+ cards[card_index].should_quit = true; // Unblock thread.
+ cards[card_index].new_data_ready_changed.notify_all();
+ }
+ cards[card_index].usb->stop_dequeue_thread();
+ }
+}
+
+namespace {
+
+int unwrap_timecode(uint16_t current_wrapped, int last)
+{
+ uint16_t last_wrapped = last & 0xffff;
+ if (current_wrapped > last_wrapped) {
+ return (last & ~0xffff) | current_wrapped;
+ } else {
+ return 0x10000 + ((last & ~0xffff) | current_wrapped);
+ }
+}
+
+} // namespace
+
+void Mixer::bm_frame(int card_index, uint16_t timecode,
+ FrameAllocator::Frame video_frame, size_t video_offset, uint16_t video_format,
+ FrameAllocator::Frame audio_frame, size_t audio_offset, uint16_t audio_format)
+{
+ CaptureCard *card = &cards[card_index];
+
+ if (audio_frame.len - audio_offset > 30000) {
+ printf("Card %d: Dropping frame with implausible audio length (len=%d, offset=%d) [timecode=0x%04x video_len=%d video_offset=%d video_format=%x)\n",
+ card_index, int(audio_frame.len), int(audio_offset),
+ timecode, int(video_frame.len), int(video_offset), video_format);
+ if (video_frame.owner) {
+ video_frame.owner->release_frame(video_frame);
+ }
+ if (audio_frame.owner) {
+ audio_frame.owner->release_frame(audio_frame);
+ }
+ return;
+ }
+
+ // Convert the audio to stereo fp32 and add it.
+ size_t num_samples = (audio_frame.len - audio_offset) / 8 / 3;
+ vector<float> audio;
+ audio.resize(num_samples * 2);
+ convert_fixed24_to_fp32(&audio[0], 2, audio_frame.data + audio_offset, 8, num_samples);
+
+ int unwrapped_timecode = timecode;
+ int dropped_frames = 0;
+ if (card->last_timecode != -1) {
+ unwrapped_timecode = unwrap_timecode(unwrapped_timecode, card->last_timecode);
+ dropped_frames = unwrapped_timecode - card->last_timecode - 1;
+ }
+ card->last_timecode = unwrapped_timecode;
+
+ // Add the audio.
+ {
+ unique_lock<mutex> lock(card->audio_mutex);
+
+ int unwrapped_timecode = timecode;
+ if (dropped_frames > 60 * 2) {
+ fprintf(stderr, "Card %d lost more than two seconds (or time code jumping around), resetting resampler\n",
+ card_index);
+ card->resampler.reset(new Resampler(48000.0, 48000.0, 2));
+ } else if (dropped_frames > 0) {
+ // Insert silence as needed.
+ fprintf(stderr, "Card %d dropped %d frame(s) (before timecode 0x%04x), inserting silence.\n",
+ card_index, dropped_frames, timecode);
+ vector<float> silence;
+ silence.resize((48000 / 60) * 2);
+ for (int i = 0; i < dropped_frames; ++i) {
+ card->resampler->add_input_samples((unwrapped_timecode - dropped_frames + i) / 60.0, silence.data(), (48000 / 60));
+ }
+ }
+ card->resampler->add_input_samples(unwrapped_timecode / 60.0, audio.data(), num_samples);
+ }
+
+ // Done with the audio, so release it.
+ if (audio_frame.owner) {
+ audio_frame.owner->release_frame(audio_frame);
+ }
+
+ {
+ // Wait until the previous frame was consumed.
+ unique_lock<mutex> lock(bmusb_mutex);
+ card->new_data_ready_changed.wait(lock, [card]{ return !card->new_data_ready || card->should_quit; });
+ if (card->should_quit) return;
+ }
+
+ if (video_frame.len - video_offset != 1280 * 750 * 2) {
+ if (video_frame.len != 0) {
+ printf("Card %d: Dropping video frame with wrong length (%ld)\n",
+ card_index, video_frame.len - video_offset);
+ }
+ if (video_frame.owner) {
+ video_frame.owner->release_frame(video_frame);
+ }
+
+ // 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);
+ card->new_data_ready = true;
+ card->new_frame = RefCountedFrame(FrameAllocator::Frame());
+ card->new_data_ready_fence = nullptr;
+ card->dropped_frames = dropped_frames;
+ card->new_data_ready_changed.notify_all();
+ }
+ return;
+ }
+
+ const PBOFrameAllocator::Userdata *userdata = (const PBOFrameAllocator::Userdata *)video_frame.userdata;
+ GLuint pbo = userdata->pbo;