📄 play_capture_hdmi.c
字号:
88200, 768000, 96000, 0, 176400, 12000, 192000, 0 }; static const RMuint32 fs_tab_hdmi[16] = { 44100, 0, 48000, 32000, 0, 0, 0, 0, 88200, 768000, 96000, 0, 176400, 0, 192000, 0 }; RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (pHDMI == NULL) return RM_FATALINVALIDPOINTER; if (pSamplingRate == NULL) return RM_FATALINVALIDPOINTER; // remember previous SamplingRate prev_fs = *pSamplingRate; // default SamplingRate to 0 *pSamplingRate = 0; // transmission is not in HDMI mode, no audio expected if (pHDMI->hdmi_mode != capsam_hdmi_mode_HDMI){ printf("XXXXXXXXXXX not HDMI mode\n"); return RM_OK; } // chip specific processing switch (pHDMI->chip) { case capsam_chip_SiI9031: // start audio procesing err = capsam_SiI9031_init_audio_clock(pHDMI->pSiI9031, TRUE, 256); // TODO if (RMFAILED(err)) return err; // read channel status err = capsam_SiI9031_get_audio_channel_status(pHDMI->pSiI9031, ch_stat); if (RMFAILED(err)) return err; fprintf(stderr, "[hdmi] audio channel status [39:0]: %02X.%02X.%02X.%02X.%02X\n", ch_stat[4], ch_stat[3], ch_stat[2], ch_stat[1], ch_stat[0]); // read N/CTS pair, CTS = (f(TMDS clk) * N) / (128 * f(s)) err = capsam_SiI9031_get_audio_n_cts(pHDMI->pSiI9031, &n, &cts, &fs_n_cts); if (RMFAILED(err)) return err; fprintf(stderr, "[hdmi] N/CTS: %lu/%lu\n", n, cts); break; case capsam_chip_AD9380: RMDBGLOG((ENABLE, "XXXXXXXXXXXXXXXXXXXXXXX %s\n",__func__)); err = capsam_AD9380_get_audio_channel_status(pHDMI->pAD9380, ch_stat); break; default: return RM_ERROR; } // not consumer use, or not mode 00 if ((ch_stat[0] & 0xC1) != 0x00) return RM_OK; // extract fs tag from channel status pclk = ch_stat[3] & 0x0F; // no audio present if (pclk == 1) return RM_OK; if (n && cts) { // match approximate SamplingRate from pixel clock and N/CTS to nearest valid sample rate if (fs_n_cts) { RMuint32 i, fs, fs_mult, fs_mod; static const RMuint32 fs_base[] = {8000, 11025, 12000, 32000, 44100, 48000}; fprintf(stderr, "[hdmi] Audio fs from N/CTS: %lu (Approx.)\n", fs_n_cts); fs = fs_n_cts; for (i = (obey_hdmi ? 3 : 0); i < sizeof(fs_base) / sizeof(RMuint32); i++) { fs_mult = fs / fs_base[i]; fs_mod = fs % fs_base[i]; if (fs_mod < (fs_base[i] / 8)) { fs_n_cts = fs_base[i] * fs_mult; break; } else if (fs_mod > (fs_base[i] * 7 / 8)) { fs_mult++; fs_n_cts = fs_base[i] * fs_mult; break; } } fprintf(stderr, "[hdmi] Audio fs from N/CTS: %lu\n", fs_n_cts); } // SamplingRate from fs tag *pSamplingRate = obey_hdmi ? fs_tab_hdmi[pclk] : fs_tab[pclk]; if (*pSamplingRate != prev_fs) { fprintf(stderr, "[hdmi] Audio fs from tag: %s, tag=0x%01lX\n", (pclk == 0) ? "44.1 kHz" : (pclk == 1) ? "<not indicated>" : (pclk == 2) ? "48 kHz" : (pclk == 3) ? "32 kHz" : (pclk == 4) ? "22.05 kHz <invalid>" : (pclk == 5) ? "11.025 kHz <invalid>" : (pclk == 6) ? "24 kHz <invalid>" : (pclk == 7) ? "16 kHz <invalid>" : (pclk == 8) ? "88.2 kHz" : (pclk == 9) ? "768 kHz" : (pclk == 10) ? "96 kHz" : (pclk == 11) ? "<reserved>" : (pclk == 12) ? "176.4 kHz" : (pclk == 13) ? "12 kHz <invalid>" : (pclk == 14) ? "192 kHz" : (pclk == 15) ? "<unknown>" : "-", pclk); } // mismatch between fs tag and N/CTS, use the latter if (fs_n_cts && (*pSamplingRate != fs_n_cts)) { fprintf(stderr, "[hdmi] ERROR: fs tag in channel status does not match frequency from N/CTS PLL!!!\n"); *pSamplingRate = fs_n_cts; } } else { // no audio *pSamplingRate = 0; } if (*pSamplingRate != prev_fs) { fprintf(stderr, "\n\n\t[hdmi] Audio fs: %lu", *pSamplingRate / 1000); if (*pSamplingRate % 1000) { fprintf(stderr, ".%03lu", *pSamplingRate % 1000); } fprintf(stderr, " kHz\n\n\n"); } return RM_OK;}/*** Mode Detection functions ***//* Detect HDMI or DVI mode from the chip, signaling necessary actions */RMstatus capsam_hdmi_detect_mode( struct capsam_hdmi_instance *pHDMI, enum capsam_hdmi_mode *pHDMIMode, RMbool *pUpdateAVI, // need to handle AVI Info Frame again RMbool *pUpdateAudio, // need to re-start audio RMbool *pUpdateVideo) // need to detect video mode{ RMstatus err; enum capsam_hdmi_mode hdmi_mode; switch (pHDMI->chip) { case capsam_chip_SiI9031: err = capsam_SiI9031_detect_mode(pHDMI->pSiI9031, &hdmi_mode); break; case capsam_chip_AD9380: err = capsam_AD9380_detect_mode(pHDMI->pAD9380, &hdmi_mode); break; default: err = RM_ERROR; } if (RMFAILED(err)) return err; if (pHDMIMode) *pHDMIMode = hdmi_mode; if (hdmi_mode != pHDMI->hdmi_mode) { pHDMI->hdmi_mode = hdmi_mode; fprintf(stderr, "[hdmi] %s mode detected\n", (hdmi_mode == capsam_hdmi_mode_HDMI) ? "HDMI" : (hdmi_mode == capsam_hdmi_mode_DVI) ? "DVI" : "unknown"); if (hdmi_mode == capsam_hdmi_mode_HDMI) { fprintf(stderr, "[hdmi] re-reading AVI InfoFrame\n"); if (pUpdateAVI) *pUpdateAVI = TRUE; if (pUpdateAudio) *pUpdateAudio = TRUE; } else if (hdmi_mode == capsam_hdmi_mode_DVI) { fprintf(stderr, "[hdmi] re-detecting video mode\n"); if (pUpdateVideo) *pUpdateVideo = TRUE; } switch (pHDMI->chip) { case capsam_chip_SiI9031: err = capsam_SiI9031_set_mode(pHDMI->pSiI9031, hdmi_mode); break; case capsam_chip_AD9380: err = RM_OK; // TODO break; default: err = RM_ERROR; break; } } return err;}/* set up certain capture parameters to match DVI capture mode (RGB888) */RMstatus capsam_hdmi_prepare_dvi_mode( struct capsam_hdmi_instance *pHDMI, enum EMhwlibTVStandard TVStandard, RMbool Wide, enum EMhwlibColorSpace *pInputColorSpace, enum EMhwlibInputColorFormat *pInputColorFormat, RMbool *pUpsample422, RMuint32 *pZoomX, RMuint32 *pZoomY, RMuint32 *pZoomW, RMuint32 *pZoomH, struct EMhwlibAspectRatio *pPictureAspectRatio){ struct EMhwlibAspectRatio PictureAspectRatio; if (pInputColorSpace) *pInputColorSpace = EMhwlibColorSpace_RGB_0_255; if (pInputColorFormat) *pInputColorFormat = EMhwlibInputColorFormat_24BPP; if (pUpsample422) *pUpsample422 = FALSE; if (pZoomX) *pZoomX = ZOOM_0; if (pZoomY) *pZoomY = ZOOM_0; if (pZoomW) *pZoomW = ZOOM_1; if (pZoomH) *pZoomH = ZOOM_1; // determine aspect ratio from video mode if (pPictureAspectRatio) { capsam_get_aspect_ratio_from_video_mode( TVStandard, NULL, Wide, &(PictureAspectRatio.X), &(PictureAspectRatio.Y)); fprintf(stderr, "[hdmi] Aspect Ratio from Video Mode: %lu:%lu\n", PictureAspectRatio.X, PictureAspectRatio.Y); *pPictureAspectRatio = PictureAspectRatio; } return RM_OK;}/*** Info Frame functions ***//*** Reading Info Frames ***//* read an info frame of the specified 'type' from the selected chip *//* set 'version' to a specific requested version, or to 0 for any */RMstatus capsam_hdmi_read_info_frame( struct capsam_hdmi_instance *pHDMI, RMuint8 type, RMuint8 version, RMuint8 *header, RMuint8 *data, RMuint32 size, RMbool honor_checksum){ RMstatus err; RMuint32 i, checksum; RMuint8 offset, next_type = 0; RMbool checksum_available = TRUE; RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (pHDMI == NULL) return RM_FATALINVALIDPOINTER; switch (pHDMI->chip) { case capsam_chip_SiI9031: // translate type to SiI9031 offset err = capsam_SiI9031_get_info_frame_offset(pHDMI->pSiI9031, type, &offset); if (RMFAILED(err)) return err; // read the info frame from the chip err = capsam_SiI9031_read_info_frame(pHDMI->pSiI9031, offset, header, data, size, honor_checksum); if (RMFAILED(err) || (header[0] && type && (header[0] != type))) { // not what was expected, try to prepare receive area for the requested type next_type = type; } else { // ISRC1 and ISRC2 share one receive area, alternate between them if (type == 0x05) { // ISRC1 if (RMunshiftBool(header[1], 7)) next_type = 0x06; // expect ISRC2 next } else if (type == 0x06) { // ISRC2 next_type = 0x05; // expect ISRC1 next } } if (next_type) { // only works for SPD, MPEG, ACP areas, ignore return status capsam_SiI9031_set_info_frame_receive_type(pHDMI->pSiI9031, offset, next_type); } break; case capsam_chip_AD9380: err = capsam_AD9380_read_info_frame(pHDMI->pAD9380, type, header, data, size); checksum_available = FALSE; break; default: err = RM_ERROR; } if (RMFAILED(err)){ RMDBGLOG((FUNCNAME, "%s return ERROR \n",__func__)); return err; } if(checksum_available){ if (header[0] & 0x80) { // CEA861 info frame: calculate checksum checksum = 0; for (i = 0; i < 3; i++) checksum += header[i]; for (i = 0; i < RMmin((RMuint32)header[2] + 1, size); i++) checksum += data[i]; } else { // HDMI packet: no checksum checksum = 0; } if (checksum & 0xFF) { if (header[0]) { fprintf(stderr, "info frame checksum error at 0x%02X, type 0x%02X: 0x%02lX\n", offset, header[0], checksum & 0xFF); } if (honor_checksum) return RM_ERROR; } if ((type && (header[0] != type)) || (version && (header[1] != version))) { if (header[0]) { RMDBGLOG((ENABLE, "Error, wrong Info Frame Type/Version: 0x%02X/0x%02X\n", header[0], header[1])); RMDBGPRINT((ENABLE, "type=0x%02lX ", header[0])); RMDBGPRINT((ENABLE, " ver=0x%02lX ", header[1])); RMDBGPRINT((ENABLE, " len=0x%02lX ", header[2])); RMDBGPRINT((ENABLE, " chk=0x%02X ", data[0])); for (i = 1; i < size; i++) { RMDBGPRINT((ENABLE, "%4l=0x%02X ", i, data[i])); } RMDBGPRINT((ENABLE, "\n")); } if (honor_checksum) return RM_ERROR; } } return RM_OK;}/* signal intent to read the next available info frame of the specified 'type' */RMstatus capsam_hdmi_prepare_info_frame( struct capsam_hdmi_instance *pHDMI, RMuint8 type){ RMstatus err; RMuint8 offset; RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (pHDMI == NULL) return RM_FATALINVALIDPOINTER; switch (pHDMI->chip) { case capsam_chip_SiI9031: // translate type to SiI9031 offset err = capsam_SiI9031_get_info_frame_offset(pHDMI->pSiI9031, type, &offset); if (RMFAILED(err)) return err; // prepare receive area err = capsam_SiI9031_set_info_frame_receive_type(pHDMI->pSiI9031, offset, type); break; case capsam_chip_AD9380: err = RM_NOTIMPLEMENTED; // TODO break; default: err = RM_ERROR; } return err;}/*** Parsing Info Frames ***//* parses an AVI data block into a struct capsam_hdmi_avi_info */void capsam_hdmi_parse_avi_struct(RMuint8 *avi_data, struct capsam_hdmi_avi_info *avi){ RMDBGLOG((FUNCNAME, "%s\n",__func__)); avi->valid = TRUE; avi->color_format = (avi_data[1] >> 5) & 0x03; avi->active_format = (avi_data[1] & 0x10) | (avi_data[2] & 0x0F); avi->h_bar = (avi_data[1] & 0x08) ? TRUE : FALSE; avi->v_bar = (avi_data[1] & 0x04) ? TRUE : FALSE; avi->scan_info = avi_data[1] & 0x03; avi->color_space = (avi_data[2] >> 6) & 0x03; avi->aspect_ratio = (avi_data[2] >> 4) & 0x03; avi->scaling = avi_data[3] & 0x03; avi->quantisation = (avi_data[3] >> 2) & 0x03; avi->ext_col = (avi_data[3] >> 4) & 0x07; avi->it_content = (avi_data[3] & 0x80) ? TRUE : FALSE; avi->vic = avi_data[4] & 0x7F; avi->pixel_rep = avi_data[5] & 0x0F; avi->top = (avi_data[7] << 8) | avi_data[6]; avi->bottom = (avi_data[9] << 8) | avi_data[8]; avi->left = (avi_data[11] << 8) | avi_data[10]; avi->right = (avi_data[13] << 8) | avi_data[12];}/* debug print of the AVI Info Frame, formatted same as the table in CEA-861 */RMstatus capsam_hdmi_print_avi_info_frame(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -