#include "sthreadwatchvideolink.h" #include #include #include /* low-level i/o */ #include #include #include // strerrno #include #include #include #include #include //#include #include #include #include "mainwindow.h" #include "commondata.h" /* * The 24-bit IEEE Registration Identifier for the HDMI-LLC Vendor * Specific Data Block. */ #define HDMI_VSDB_EXT_TAG 0x000c03 /* * The 24-bit IEEE Registration Identifier for the HDMI-Forum Vendor * Specific Data Block. */ #define HF_VSDB_EXT_TAG 0xc45dd8 #define VID_CAP_EXT_TAG 0 #define COLORIMETRY_EXT_TAG 5 #define HDR_MD_EXT_TAG 6 #define VSDB_TAG 3 #define SPEAKER_TAG 4 #define EXTENDED_TAG 7 enum format { HEX, RAW, CARRAY }; static struct v4l2_edid sedid; static struct v4l2_edid info_edid; static enum format gformat; static enum format sformat; static unsigned clear_pad; static long phys_addr = -1; static __u8 toggle_cta861_hdr_flags; #define CTA861_HDR_UNDERSCAN (1 << 6) #define CTA861_HDR_AUDIO (1 << 6) #define CTA861_HDR_YCBCR444 (1 << 5) #define CTA861_HDR_YCBCR422 (1 << 4) static __u8 toggle_speaker1_flags; #define SPEAKER1_FL_FR (1 << 0) #define SPEAKER1_LFE (1 << 1) #define SPEAKER1_FC (1 << 2) #define SPEAKER1_BL_BR (1 << 3) #define SPEAKER1_BC (1 << 4) #define SPEAKER1_FLC_FRC (1 << 5) #define SPEAKER1_RLC_RRC (1 << 6) #define SPEAKER1_FLW_FRW (1 << 7) static __u8 toggle_speaker2_flags; #define SPEAKER2_TPFL_TPFR (1 << 0) #define SPEAKER2_TPC (1 << 1) #define SPEAKER2_TPFC (1 << 2) #define SPEAKER2_LS_RS (1 << 3) #define SPEAKER2_LFE2 (1 << 4) #define SPEAKER2_TPBC (1 << 5) #define SPEAKER2_SIL_SIR (1 << 6) #define SPEAKER2_TPSIL_TPSIR (1 << 7) static __u8 toggle_speaker3_flags; #define SPEAKER3_TPBL_TPBR (1 << 0) #define SPEAKER3_BTFC (1 << 1) #define SPEAKER3_BTFL_BTFR (1 << 2) #define SPEAKER3_TPLS_TPRS (1 << 3) static __u8 toggle_hdmi_vsdb_dc_flags; #define HDMI_VSDB_Y444_BIT (1 << 3) #define HDMI_VSDB_30_BIT (1 << 4) #define HDMI_VSDB_36_BIT (1 << 5) #define HDMI_VSDB_48_BIT (1 << 6) static __u8 toggle_hdmi_vsdb_cnc_flags; #define HDMI_VSDB_GRAPHICS (1 << 0) #define HDMI_VSDB_PHOTO (1 << 1) #define HDMI_VSDB_CINEMA (1 << 2) #define HDMI_VSDB_GAME (1 << 3) #define HDMI_VSDB_I_LATENCY (1 << 6) #define HDMI_VSDB_LATENCY (1 << 7) static __u8 toggle_hf_vsdb_flags; #define HF_VSDB_SCSD_PRESENT (1 << 7) static int mod_s_pt = -1; static int mod_s_it = -1; static int mod_s_ce = -1; static __u8 toggle_vid_cap_flags; #define VID_CAP_QS (1 << 6) #define VID_CAP_QY (1 << 7) static __u8 toggle_colorimetry_flags1; #define COLORIMETRY_XVYCC601 (1 << 0) #define COLORIMETRY_XVYCC709 (1 << 1) #define COLORIMETRY_SYCC (1 << 2) #define COLORIMETRY_ADOBEYCC (1 << 3) #define COLORIMETRY_ADOBERGB (1 << 4) #define COLORIMETRY_BT2020CYCC (1 << 5) #define COLORIMETRY_BT2020YCC (1 << 6) #define COLORIMETRY_BT2020RGB (1 << 7) static __u8 toggle_colorimetry_flags2; #define COLORIMETRY_DCIP3 (1 << 0) static __u8 toggle_hdr_md_flags; #define HDR_MD_SDR (1 << 0) #define HDR_MD_HDR (1 << 1) #define HDR_MD_SMPTE_2084 (1 << 2) #define HDR_MD_HLG (1 << 3) static void edid_add_block(struct v4l2_edid *e) { e->blocks++; if (e->blocks > 256) { fprintf(stderr, "edid file error: too long\n"); free(e->edid); e->edid = NULL; exit(1); } e->edid = (unsigned char *)realloc(e->edid, e->blocks * 128); } static void read_edid_file(FILE *f, struct v4l2_edid *e) { char value[3] = { 0 }; unsigned i = 0; int c; fseek(f, SEEK_SET, 0); e->edid = NULL; e->blocks = 0; while ((c = fgetc(f)) != EOF) { if (sformat == RAW) { if (i % 256 == 0) edid_add_block(e); e->edid[i / 2] = c; i += 2; continue; } /* Handle '0x' prefix */ if ((i & 1) && value[0] == '0' && (c == 'x' || c == 'X')) i--; if (!isxdigit(c)) continue; if (i & 0x01) { value[1] = c; if (i % 256 == 1) edid_add_block(e); e->edid[i / 2] = strtoul(value, 0, 16); } else { value[0] = c; } i++; } } static unsigned char crc_calc(const unsigned char *b) { unsigned char sum = 0; int i; for (i = 0; i < 127; i++) sum += b[i]; return 256 - sum; } static bool crc_ok(const unsigned char *b) { return crc_calc(b) == b[127]; } static void fix_edid(struct v4l2_edid *e) { for (unsigned b = 0; b < e->blocks; b++) { unsigned char *buf = e->edid + 128 * b; if (!crc_ok(buf)) buf[127] = crc_calc(buf); } } static bool verify_edid(struct v4l2_edid *e) { bool valid = true; for (unsigned b = 0; b < e->blocks; b++) { const unsigned char *buf = e->edid + 128 * b; if (!crc_ok(buf)) { fprintf(stderr, "Block %u has a checksum error (should be 0x%02x)\n", b, crc_calc(buf)); valid = false; } } return valid; } static int get_edid_tag_location(const unsigned char *edid, unsigned size, unsigned char want_tag, __u32 ext_tag) { unsigned char d; if (size < 256) return -1; if (edid[0x7e] != 1 || edid[0x80] != 0x02 || edid[0x81] != 0x03) return -1; /* search tag */ d = edid[0x82] & 0x7f; if (d <= 4) return -1; int i = 0x84; int end = 0x80 + d; do { unsigned char tag = edid[i] >> 5; unsigned char len = edid[i] & 0x1f; if (tag != want_tag || i + len > end) { i += len + 1; continue; } /* * Tag 3 (Vendor-Specific Data Block) has * a 24 bit IEEE identifier. */ if (tag == VSDB_TAG && len >= 3 && edid[i + 1] == (ext_tag & 0xff) && edid[i + 2] == ((ext_tag >> 8) & 0xff) && edid[i + 3] == ((ext_tag >> 16) & 0xff)) return i; /* * Tag 7 has an extended tag, others (0-2, 4-6) * have no identifiers. */ if ((tag < EXTENDED_TAG && tag != VSDB_TAG) || (tag == EXTENDED_TAG && len >= 1 && edid[i + 1] == ext_tag)) return i; i += len + 1; } while (i < end); return -1; } static int get_edid_cta861_hdr_location(const unsigned char *edid, unsigned size) { if (size < 256) return -1; if (edid[0x7e] != 1 || edid[0x80] != 0x02 || edid[0x81] != 0x03) return -1; return 0x83; } static int get_edid_spa_location(const unsigned char *edid, unsigned size) { int loc = get_edid_tag_location(edid, size, VSDB_TAG, HDMI_VSDB_EXT_TAG); if (loc < 0) return loc; return (edid[loc] & 0x1f) >= 5 ? loc + 4 : -1; } static int get_edid_hdmi_vsdb_location(const unsigned char *edid, unsigned size) { int loc = get_edid_tag_location(edid, size, VSDB_TAG, HDMI_VSDB_EXT_TAG); if (loc < 0) return loc; return (edid[loc] & 0x1f) >= 5 ? loc : -1; } static int get_edid_hf_vsdb_location(const unsigned char *edid, unsigned size) { int loc = get_edid_tag_location(edid, size, VSDB_TAG, HF_VSDB_EXT_TAG); if (loc < 0) return loc; return (edid[loc] & 0x1f) >= 6 ? loc + 5 : -1; } static int get_edid_speaker_location(const unsigned char *edid, unsigned size) { int loc = get_edid_tag_location(edid, size, SPEAKER_TAG, 0); if (loc < 0) return loc; return (edid[loc] & 0x1f) >= 3 ? loc + 1 : -1; } static int get_edid_vid_cap_location(const unsigned char *edid, unsigned size) { int loc = get_edid_tag_location(edid, size, EXTENDED_TAG, VID_CAP_EXT_TAG); if (loc < 0) return loc; return (edid[loc] & 0x1f) >= 2 ? loc + 2 : -1; } static int get_edid_colorimetry_location(const unsigned char *edid, unsigned size) { int loc = get_edid_tag_location(edid, size, EXTENDED_TAG, COLORIMETRY_EXT_TAG); if (loc < 0) return loc; return (edid[loc] & 0x1f) >= 3 ? loc + 2 : -1; } static int get_edid_hdr_md_location(const unsigned char *edid, unsigned size) { int loc = get_edid_tag_location(edid, size, EXTENDED_TAG, HDR_MD_EXT_TAG); if (loc < 0) return loc; return (edid[loc] & 0x1f) >= 3 ? loc + 2 : -1; } static void set_edid_phys_addr(unsigned char *edid, unsigned size, unsigned short phys_addr) { int loc = get_edid_spa_location(edid, size); unsigned char sum = 0; int i; if (loc < 0) return; edid[loc] = phys_addr >> 8; edid[loc + 1] = phys_addr & 0xff; loc &= ~0x7f; for (i = loc; i < loc + 127; i++) sum += edid[i]; edid[i] = 256 - sum; } static unsigned short get_edid_phys_addr(const unsigned char *edid, unsigned size) { int loc = get_edid_spa_location(edid, size); if (loc < 0) return 0xffff; return (edid[loc] << 8) | edid[loc + 1]; } static unsigned short parse_phys_addr(const char *value) { unsigned p1, p2, p3, p4; if (!strchr(value, '.')) return strtoul(value, NULL, 0); if (sscanf(value, "%x.%x.%x.%x", &p1, &p2, &p3, &p4) != 4) { fprintf(stderr, "Expected a physical address of the form x.x.x.x\n"); return 0xffff; } if (p1 > 0xf || p2 > 0xf || p3 > 0xf || p4 > 0xf) { fprintf(stderr, "Physical address components should never be larger than 0xf\n"); return 0xffff; } return (p1 << 12) | (p2 << 8) | (p3 << 4) | p4; } SThreadWatchVideoLink::SThreadWatchVideoLink():QSThread() { m_nCurrentState = 0; FILE *fin = NULL; bool must_fix_edid = true; int loc = 0; fin = fopen("/home/birdhead/test.edid", "r"); if (fin) { read_edid_file(fin, &sedid); if (sedid.blocks == 0) { //Error!!!! } } if (toggle_cta861_hdr_flags || phys_addr >= 0) { loc = get_edid_cta861_hdr_location(sedid.edid, sedid.blocks * 128); if (loc >= 0) { sedid.edid[loc] ^= toggle_cta861_hdr_flags; if (phys_addr >= 0) set_edid_phys_addr(sedid.edid, sedid.blocks * 128, phys_addr); must_fix_edid = true; } } if (toggle_speaker1_flags || toggle_speaker2_flags || toggle_speaker3_flags) { loc = get_edid_speaker_location(sedid.edid, sedid.blocks * 128); if (loc >= 0) { sedid.edid[loc] ^= toggle_speaker1_flags; sedid.edid[loc + 1] ^= toggle_speaker2_flags; sedid.edid[loc + 2] ^= toggle_speaker3_flags; must_fix_edid = true; } } if (toggle_hdmi_vsdb_dc_flags || toggle_hdmi_vsdb_cnc_flags) { loc = get_edid_hdmi_vsdb_location(sedid.edid, sedid.blocks * 128); if (loc >= 0) { __u8 len = sedid.edid[loc] & 0x1f; if (len >= 6) { sedid.edid[loc + 6] ^= toggle_hdmi_vsdb_dc_flags; must_fix_edid = true; } if (len >= 8) { sedid.edid[loc + 8] ^= toggle_hdmi_vsdb_cnc_flags; must_fix_edid = true; } } } if (toggle_hf_vsdb_flags) { loc = get_edid_hf_vsdb_location(sedid.edid, sedid.blocks * 128); if (loc >= 0) { sedid.edid[loc + 1] ^= toggle_hf_vsdb_flags; must_fix_edid = true; } } if (toggle_vid_cap_flags || mod_s_pt >= 0 || mod_s_ce >= 0 || mod_s_it >= 0) { loc = get_edid_vid_cap_location(sedid.edid, sedid.blocks * 128); if (loc >= 0) { sedid.edid[loc] ^= toggle_vid_cap_flags; if (mod_s_ce >= 0) { sedid.edid[loc] &= 0xfc; sedid.edid[loc] |= mod_s_ce << 0; } if (mod_s_it >= 0) { sedid.edid[loc] &= 0xf3; sedid.edid[loc] |= mod_s_it << 2; } if (mod_s_pt >= 0) { sedid.edid[loc] &= 0xcf; sedid.edid[loc] |= mod_s_pt << 4; } must_fix_edid = true; } } if (toggle_colorimetry_flags1 || toggle_colorimetry_flags2) { loc = get_edid_colorimetry_location(sedid.edid, sedid.blocks * 128); if (loc >= 0) { sedid.edid[loc] ^= toggle_colorimetry_flags1; sedid.edid[loc + 1] ^= toggle_colorimetry_flags2; must_fix_edid = true; } } if (toggle_hdr_md_flags) { loc = get_edid_hdr_md_location(sedid.edid, sedid.blocks * 128); if (loc >= 0) { sedid.edid[loc] ^= toggle_hdr_md_flags; must_fix_edid = true; } } if (must_fix_edid) fix_edid(&sedid); //print_edid_mods(&sedid); if (verify_edid(&sedid)) { //doioctl(fd, VIDIOC_S_EDID, &sedid); } else { fprintf(stderr, "EDID not set due to checksum errors\n"); } if (fin) { fclose(fin); } } SThreadWatchVideoLink::~SThreadWatchVideoLink() { if (sedid.edid) { free(sedid.edid); sedid.edid = NULL; } ExitThread(); } void SThreadWatchVideoLink::Init() { #ifdef _PC m_nCurrentState = 0x00; #else //m_nCurrentState = 0x00; m_nCurrentState = 0x01; #endif m_TimerEDID.restart(); } void SThreadWatchVideoLink::InnerRun() { if(m_nCurrentState&0x01) { WatchVideoConnect(); usleep(100*1000); } else { //usleep(100*1000); usleep(200*1000); } } int SThreadWatchVideoLink::WatchVideoConnect() { int nCaptureMode = 0; CommonData* pCommonData = MainWindow::GetCommonData(); if(pCommonData==NULL) { return -1; } if(m_pThreadRTSP!=NULL) { nCaptureMode = m_pThreadRTSP->GetCaptureMode(); } std::string strDevice = "/dev/video0"; int fd = open_device(strDevice); int input = 1; bool bConnectHDMI = false; if (xioctl(fd, VIDIOC_G_INPUT, &input) == 0) { struct v4l2_input vin; //printf("Video input set to %d", input); vin.index = input; if (xioctl(fd, VIDIOC_ENUMINPUT, &vin) >= 0) { //printf(" (%s)", vin.name); } //printf("\n"); close(fd); bool bPrevIsConnectHDMI = false; bPrevIsConnectHDMI = pCommonData->IsConnectHDMI(); if(vin.status & V4L2_IN_ST_NO_SIGNAL) { bool bExecStop = false; if(m_nRTSPStatus==1) { bExecStop = true; m_nRTSPStatus = 0; // Stop RTSP } if(m_pThreadRTSP!=NULL) { if(m_pThreadRTSP->GetState()==0) { if(bExecStop==true) { //qDebug() << "StopRTSP!!!!!!!!!!!!!!!!!"; //m_pThreadRTSP->StopRTSP(); } } } if(bPrevIsConnectHDMI==true) { //HDMICalbeConnect(false); //pCommonData->SetConnectHDMI(false); m_TimerEDID.restart(); } else { //HDMICalbeConnect(true); if(m_TimerEDID.elapsed()>= 360*1000) //360 seconds { //QProcess::execute("/home/birdhead/edid.sh"); m_TimerEDID.restart(); } } } else { /* bool bExecStart = false; if(m_nRTSPStatus==0) { bExecStart = true; m_nRTSPStatus = 1; //Playing } if(m_pThreadRTSP!=NULL) { if(m_pThreadRTSP->GetState()==-1) { m_nCaptureMode = m_pThreadRTSP->GetCaptureMode(); m_pThreadRTSP->ChangeState(0); } else if(m_pThreadRTSP->GetState()==11 || bExecStart==true) { m_nCaptureMode = m_pThreadRTSP->GetCaptureMode(); m_pThreadRTSP->ChangeState(0); } } */ if(bPrevIsConnectHDMI==false) { //HDMICalbeConnect(true); //pCommonData->SetConnectHDMI(true); m_TimerEDID.restart(); } bConnectHDMI = true; } } else { close(fd); } m_Lock.lock(); m_bConnectHDMI = bConnectHDMI; m_Lock.unlock(); return 0; } bool SThreadWatchVideoLink::IsConnectHDMI() { bool bConnectHDMI = false; m_Lock.lock(); bConnectHDMI = m_bConnectHDMI; //bConnectHDMI = true; m_Lock.unlock(); return bConnectHDMI; } int SThreadWatchVideoLink::open_device(std::string strDevice) { struct stat st; int fd = 0; if (-1 == stat(strDevice.c_str(), &st)) { return -1; } if (!S_ISCHR(st.st_mode)) { return -2; } fd = open(strDevice.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0); if (-1 == fd) { return -3; } return fd; } int SThreadWatchVideoLink::xioctl(int fh, unsigned long int request, void *arg) { int r = 0; int nCount = 0; do { r = ioctl(fh, request, arg); nCount++; } while (-1 == r && EINTR == errno && nCount<50); if(nCount>=50) { r = -1; } return r; } void SThreadWatchVideoLink::SetThreadRTSP(QRTSPThread* pThread) { m_pThreadRTSP = pThread; }