}
}
-// Returns length of a frame with the given format, in TIMEBASE units.
-int64_t find_frame_length(uint16_t video_format)
-{
- if (video_format == 0x0800) {
- // No video signal. These green pseudo-frames seem to come at about 30.13 Hz.
- // It's a strange thing, but what can you do.
- return TIMEBASE * 100 / 3013;
- }
- if ((video_format & 0xe800) != 0xe800) {
- printf("Video format 0x%04x does not appear to be a video format. Assuming 60 Hz.\n",
- video_format);
- return TIMEBASE / 60;
- }
-
- // 0x8 seems to be a flag about availability of deep color on the input,
- // except when it's not (e.g. it's the only difference between NTSC 23.98
- // and PAL). Rather confusing. But we clear it here nevertheless, because
- // usually it doesn't mean anything.
- //
- // We don't really handle interlaced formats at all yet.
- uint16_t normalized_video_format = video_format & ~0xe808;
- if (normalized_video_format == 0x0143) { // 720p50.
- return TIMEBASE / 50;
- } else if (normalized_video_format == 0x0103) { // 720p60.
- return TIMEBASE / 60;
- } else if (normalized_video_format == 0x0121) { // 720p59.94.
- return TIMEBASE * 1001 / 60000;
- } else if (normalized_video_format == 0x01c3 || // 1080p30.
- normalized_video_format == 0x0003) { // 1080i60.
- return TIMEBASE / 30;
- } else if (normalized_video_format == 0x01e1 || // 1080p29.97.
- normalized_video_format == 0x0021 || // 1080i59.94.
- video_format == 0xe901 || // NTSC (480i59.94, I suppose).
- video_format == 0xe9c1 || // Ditto.
- video_format == 0xe801) { // Ditto.
- return TIMEBASE * 1001 / 30000;
- } else if (normalized_video_format == 0x0063 || // 1080p25.
- normalized_video_format == 0x0043 || // 1080i50.
- video_format == 0xe909) { // PAL (576i50, I suppose).
- return TIMEBASE / 25;
- } else if (normalized_video_format == 0x008e) { // 1080p24.
- return TIMEBASE / 24;
- } else if (normalized_video_format == 0x00a1) { // 1080p23.98.
- return TIMEBASE * 1001 / 24000;
- return TIMEBASE / 25;
- } else {
- printf("Unknown video format 0x%04x. Assuming 60 Hz.\n", video_format);
- return TIMEBASE / 60;
- }
-}
-
} // namespace
void Mixer::bm_frame(unsigned card_index, uint16_t timecode,
{
CaptureCard *card = &cards[card_index];
- int64_t frame_length = find_frame_length(video_format);
+ int width, height, frame_rate_nom, frame_rate_den;
+ bool interlaced;
+
+ decode_video_format(video_format, &width, &height, &frame_rate_nom, &frame_rate_den, &interlaced); // Ignore return value for now.
+ int64_t frame_length = TIMEBASE * frame_rate_den / frame_rate_nom;
size_t num_samples = (audio_frame.len >= audio_offset) ? (audio_frame.len - audio_offset) / 8 / 3 : 0;
if (num_samples > OUTPUT_FREQUENCY / 10) {
if (card->last_timecode != -1) {
dropped_frames = unwrap_timecode(timecode, card->last_timecode) - card->last_timecode - 1;
}
- card->last_timecode = timecode;
// Convert the audio to stereo fp32 and add it.
vector<float> audio;
{
unique_lock<mutex> lock(card->audio_mutex);
+ // Number of samples per frame if we need to insert silence.
+ // (Could be nonintegral, but resampling will save us then.)
+ int silence_samples = OUTPUT_FREQUENCY * frame_rate_den / frame_rate_nom;
+
if (dropped_frames > MAX_FPS * 2) {
- fprintf(stderr, "Card %d lost more than two seconds (or time code jumping around), resetting resampler\n",
- card_index);
+ fprintf(stderr, "Card %d lost more than two seconds (or time code jumping around; from 0x%04x to 0x%04x), resetting resampler\n",
+ card_index, card->last_timecode, timecode);
card->resampling_queue.reset(new ResamplingQueue(OUTPUT_FREQUENCY, OUTPUT_FREQUENCY, 2));
+ dropped_frames = 0;
} else if (dropped_frames > 0) {
- // Insert silence as needed. (The number of samples could be nonintegral,
- // but resampling will save us then.)
+ // 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((OUTPUT_FREQUENCY * frame_length / TIMEBASE) * 2);
+ silence.resize(silence_samples * 2);
for (int i = 0; i < dropped_frames; ++i) {
- card->resampling_queue->add_input_samples(local_pts / double(TIMEBASE), silence.data(), silence.size() / 2);
+ card->resampling_queue->add_input_samples(local_pts / double(TIMEBASE), silence.data(), silence_samples);
// Note that if the format changed in the meantime, we have
// no way of detecting that; we just have to assume the frame length
// is always the same.
local_pts += frame_length;
}
}
+ if (num_samples == 0) {
+ audio.resize(silence_samples * 2);
+ num_samples = silence_samples;
+ }
card->resampling_queue->add_input_samples(local_pts / double(TIMEBASE), audio.data(), num_samples);
card->next_local_pts = local_pts + frame_length;
}
+ card->last_timecode = timecode;
+
// Done with the audio, so release it.
if (audio_frame.owner) {
audio_frame.owner->release_frame(audio_frame);
//check_error();
// Upload the textures.
- glBindTexture(GL_TEXTURE_2D, userdata->tex_y);
+ size_t skipped_lines = 25;
+ size_t cbcr_width = WIDTH / 2;
+ size_t cbcr_offset = video_offset / 2;
+ size_t y_offset = cbcr_offset + cbcr_width * (HEIGHT + EXTRAHEIGHT) * sizeof(uint16_t) + video_offset / 2;
+
+ glBindTexture(GL_TEXTURE_2D, userdata->tex_cbcr);
check_error();
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_RED, GL_UNSIGNED_BYTE, BUFFER_OFFSET((WIDTH * (HEIGHT+EXTRAHEIGHT) * 2 + 44) / 2 + WIDTH * 25 + 22));
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, cbcr_width, HEIGHT, GL_RG, GL_UNSIGNED_BYTE, BUFFER_OFFSET(cbcr_offset + cbcr_width * skipped_lines * sizeof(uint16_t)));
check_error();
- glBindTexture(GL_TEXTURE_2D, userdata->tex_cbcr);
+ glBindTexture(GL_TEXTURE_2D, userdata->tex_y);
check_error();
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH/2, HEIGHT, GL_RG, GL_UNSIGNED_BYTE, BUFFER_OFFSET(WIDTH * 25 + 22));
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_RED, GL_UNSIGNED_BYTE, BUFFER_OFFSET(y_offset + WIDTH * skipped_lines));
check_error();
glBindTexture(GL_TEXTURE_2D, 0);
check_error();
clock_gettime(CLOCK_MONOTONIC, &start);
int frame = 0;
- int dropped_frames = 0;
+ int stats_dropped_frames = 0;
while (!should_quit) {
CaptureCard card_copy[MAX_CARDS];
int num_samples_times_timebase = OUTPUT_FREQUENCY * card->new_frame_length + card->fractional_samples;
num_samples[card_index] = num_samples_times_timebase / TIMEBASE;
card->fractional_samples = num_samples_times_timebase % TIMEBASE;
+ assert(num_samples[card_index] >= 0);
}
}
// For dropped frames, increase the pts. Note that if the format changed
// in the meantime, we have no way of detecting that; we just have to
// assume the frame length is always the same.
- ++dropped_frames;
+ ++stats_dropped_frames;
pts_int += card_copy[0].new_frame_length;
}
}
// If the first card is reporting a corrupted or otherwise dropped frame,
// just increase the pts (skipping over this frame) and don't try to compute anything new.
if (card_copy[0].new_frame->len == 0) {
- ++dropped_frames;
+ ++stats_dropped_frames;
pts_int += card_copy[0].new_frame_length;
continue;
}
1e-9 * (now.tv_nsec - start.tv_nsec);
if (frame % 100 == 0) {
printf("%d frames (%d dropped) in %.3f seconds = %.1f fps (%.1f ms/frame)\n",
- frame, dropped_frames, elapsed, frame / elapsed,
+ frame, stats_dropped_frames, elapsed, frame / elapsed,
1e3 * elapsed / frame);
// chain->print_phase_timing();
}