+ // NTSC (480i59.94, I suppose). A special case, see below.
+ if ((video_format & ~0x0800) == 0xe101 ||
+ (video_format & ~0x0800) == 0xe1c1 ||
+ (video_format & ~0x0800) == 0xe001) {
+ decoded_video_format->width = 720;
+ decoded_video_format->height = 480;
+ if (video_format & 0x0800) {
+ decoded_video_format->stride = 720 * 2;
+ } else {
+ decoded_video_format->stride = v210_stride(720);
+ }
+ decoded_video_format->extra_lines_top = 17;
+ decoded_video_format->extra_lines_bottom = 28;
+ decoded_video_format->frame_rate_nom = 30000;
+ decoded_video_format->frame_rate_den = 1001;
+ decoded_video_format->second_field_start = 280;
+ decoded_video_format->interlaced = true;
+ return true;
+ }
+
+ // PAL (576i50, I suppose). A special case, see below.
+ if ((video_format & ~0x0800) == 0xe109 ||
+ (video_format & ~0x0800) == 0xe1c9 ||
+ (video_format & ~0x0800) == 0xe009 ||
+ (video_format & ~0x0800) == 0xe3e9 ||
+ (video_format & ~0x0800) == 0xe3e1) {
+ decoded_video_format->width = 720;
+ decoded_video_format->height = 576;
+ if (video_format & 0x0800) {
+ decoded_video_format->stride = 720 * 2;
+ } else {
+ decoded_video_format->stride = v210_stride(720);
+ }
+ decoded_video_format->extra_lines_top = 22;
+ decoded_video_format->extra_lines_bottom = 27;
+ decoded_video_format->frame_rate_nom = 25;
+ decoded_video_format->frame_rate_den = 1;
+ decoded_video_format->second_field_start = 335;
+ decoded_video_format->interlaced = true;
+ return true;
+ }
+
+ // 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
+ // and PAL). Rather confusing. But we clear it here nevertheless, because
+ // usually it doesn't mean anything. 0x0800 appears to be 8-bit input
+ // (as opposed to 10-bit).
+ //
+ // 0x4 is a flag I've only seen from the D4. I don't know what it is.
+ uint16_t normalized_video_format = video_format & ~0xe80c;
+ constexpr VideoFormatEntry entries[] = {
+ { 0x01f1, 720, 480, 0, 40, 5, 60000, 1001, false }, // 480p59.94 (believed).
+ { 0x0131, 720, 576, 0, 44, 5, 50, 1, false }, // 576p50.
+ { 0x0151, 720, 576, 0, 44, 5, 50, 1, false }, // 576p50.
+ { 0x0011, 720, 576, 0, 44, 5, 50, 1, false }, // 576p50 (5:4).
+ { 0x0143, 1280, 720, 0, 25, 5, 50, 1, false }, // 720p50.
+ { 0x0103, 1280, 720, 0, 25, 5, 60, 1, false }, // 720p60.
+ { 0x0125, 1280, 720, 0, 25, 5, 60, 1, false }, // 720p60.
+ { 0x0121, 1280, 720, 0, 25, 5, 60000, 1001, false }, // 720p59.94.
+ { 0x01c3, 1920, 1080, 0, 41, 4, 30, 1, false }, // 1080p30.
+ { 0x0003, 1920, 1080, 583, 20, 25, 30, 1, true }, // 1080i60.
+ { 0x01e1, 1920, 1080, 0, 41, 4, 30000, 1001, false }, // 1080p29.97.
+ { 0x0021, 1920, 1080, 583, 20, 25, 30000, 1001, true }, // 1080i59.94.
+ { 0x0063, 1920, 1080, 0, 41, 4, 25, 1, false }, // 1080p25.
+ { 0x0043, 1920, 1080, 583, 20, 25, 25, 1, true }, // 1080i50.
+ { 0x0083, 1920, 1080, 0, 41, 4, 24, 1, false }, // 1080p24.
+ { 0x00a1, 1920, 1080, 0, 41, 4, 24000, 1001, false }, // 1080p23.98.
+ };
+ for (const VideoFormatEntry &entry : entries) {
+ if (normalized_video_format == entry.normalized_video_format) {
+ decoded_video_format->width = entry.width;
+ decoded_video_format->height = entry.height;
+ if (video_format & 0x0800) {
+ decoded_video_format->stride = entry.width * 2;
+ } else {
+ decoded_video_format->stride = v210_stride(entry.width);
+ }
+ decoded_video_format->second_field_start = entry.second_field_start;
+ decoded_video_format->extra_lines_top = entry.extra_lines_top;
+ decoded_video_format->extra_lines_bottom = entry.extra_lines_bottom;
+ decoded_video_format->frame_rate_nom = entry.frame_rate_nom;
+ decoded_video_format->frame_rate_den = entry.frame_rate_den;
+ decoded_video_format->interlaced = entry.interlaced;
+ return true;
+ }
+ }
+
+ printf("Unknown video format 0x%04x (normalized 0x%04x). Assuming 720p60.\n", video_format, normalized_video_format);
+ decoded_video_format->width = 1280;
+ decoded_video_format->height = 720;
+ decoded_video_format->stride = 1280 * 2;
+ decoded_video_format->frame_rate_nom = 60;
+ decoded_video_format->frame_rate_den = 1;
+ return false;
+}
+
+// There are seemingly no direct indicators of sample rate; you just get
+// one frame's worth and have to guess from that.
+int guess_sample_rate(const VideoFormat &video_format, size_t len, int default_rate)
+{
+ size_t num_samples = len / 3 / 8;
+ size_t num_samples_per_second = num_samples * video_format.frame_rate_nom / video_format.frame_rate_den;
+
+ // See if we match or are very close to any of the mandatory HDMI sample rates.
+ const int candidate_sample_rates[] = { 32000, 44100, 48000 };
+ for (int rate : candidate_sample_rates) {
+ if (abs(int(num_samples_per_second) - rate) < 50) {
+ return rate;
+ }
+ }
+
+ fprintf(stderr, "%ld samples at %d/%d fps (%ld Hz) matches no known sample rate, keeping capture at %d Hz\n",
+ num_samples, video_format.frame_rate_nom, video_format.frame_rate_den, num_samples_per_second, default_rate);
+ return default_rate;
+}
+
+} // namespace
+
+FrameAllocator::~FrameAllocator() {}