Media Player Classic - HC 源代码分析 4:核心类 (CMainFrame)(3)

来源:互联网 发布:淘宝棉麻女装品牌 编辑:程序博客网 时间:2024/05/16 16:13

=====================================================

Media Player Classic - HC 源代码分析系列文章列表:

Media Player Classic - HC 源代码分析 1:整体结构

Media Player Classic - HC 源代码分析 2:核心类 (CMainFrame)(1)

Media Player Classic - HC 源代码分析 3:核心类 (CMainFrame)(2)

Media Player Classic - HC 源代码分析 4:核心类 (CMainFrame)(3)

Media Player Classic - HC 源代码分析 5:关于对话框 (CAboutDlg)

Media Player Classic - HC 源代码分析 6:MediaInfo选项卡 (CPPageFileMediaInfo)

Media Player Classic - HC 源代码分析 7:详细信息选项卡(CPPageFileInfoDetails)

=====================================================



此前已经写了2篇文章介绍Media Player Classic - HC (mpc-hc)源代码中的核心类 CMainFrame:

Media Player Classic - HC 源代码分析 2:核心类 (CMainFrame)(1)

Media Player Classic - HC 源代码分析 3:核心类 (CMainFrame)(2)

此前的文章一直都是围绕着OpenMedia()以及其调用的函数进行分析的。研究的都是和文件打开有关系的功能。在这里再介绍一些其它函数。

在mpc-hc开始运行的时候,会调用OnCreate():

<span style="font-family:Arial;font-size:12px;">//刚打开的时候加载int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){    if (__super::OnCreate(lpCreateStruct) == -1) {        return -1;    }//加载菜单    m_popup.LoadMenu(IDR_POPUP);    m_popupmain.LoadMenu(IDR_POPUPMAIN);    // create a view to occupy the client area of the frame// 创建视频画面部分?    if (!m_wndView.Create(nullptr, nullptr, AFX_WS_DEFAULT_VIEW,                          CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, nullptr)) {        TRACE(_T("Failed to create view window\n"));        return -1;    }    // Should never be RTLed    m_wndView.ModifyStyleEx(WS_EX_LAYOUTRTL, WS_EX_NOINHERITLAYOUT);    // static bars//各种状态栏    BOOL bResult = m_wndStatusBar.Create(this);    if (bResult) {        bResult = m_wndStatsBar.Create(this);    }    if (bResult) {        bResult = m_wndInfoBar.Create(this);    }    if (bResult) {        bResult = m_wndToolBar.Create(this);    }    if (bResult) {        bResult = m_wndSeekBar.Create(this);    }    if (!bResult) {        TRACE(_T("Failed to create all control bars\n"));        return -1;      // fail to create    }// 各种Bar    m_pFullscreenWnd = DEBUG_NEW CFullscreenWnd(this);    m_bars.AddTail(&m_wndSeekBar);    m_bars.AddTail(&m_wndToolBar);    m_bars.AddTail(&m_wndInfoBar);    m_bars.AddTail(&m_wndStatsBar);    m_bars.AddTail(&m_wndStatusBar);    m_wndSeekBar.Enable(false);    // dockable bars// 可停靠的    EnableDocking(CBRS_ALIGN_ANY);    m_dockingbars.RemoveAll();    m_wndSubresyncBar.Create(this, AFX_IDW_DOCKBAR_TOP, &m_csSubLock);    m_wndSubresyncBar.SetBarStyle(m_wndSubresyncBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);    m_wndSubresyncBar.EnableDocking(CBRS_ALIGN_ANY);    m_wndSubresyncBar.SetHeight(200);    m_dockingbars.AddTail(&m_wndSubresyncBar);    m_wndPlaylistBar.Create(this, AFX_IDW_DOCKBAR_BOTTOM);    m_wndPlaylistBar.SetBarStyle(m_wndPlaylistBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);    m_wndPlaylistBar.EnableDocking(CBRS_ALIGN_ANY);    m_wndPlaylistBar.SetHeight(100);    m_dockingbars.AddTail(&m_wndPlaylistBar);    m_wndPlaylistBar.LoadPlaylist(GetRecentFile());    m_wndEditListEditor.Create(this, AFX_IDW_DOCKBAR_RIGHT);    m_wndEditListEditor.SetBarStyle(m_wndEditListEditor.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);    m_wndEditListEditor.EnableDocking(CBRS_ALIGN_ANY);    m_dockingbars.AddTail(&m_wndEditListEditor);    m_wndEditListEditor.SetHeight(100);    m_wndCaptureBar.Create(this, AFX_IDW_DOCKBAR_LEFT);    m_wndCaptureBar.SetBarStyle(m_wndCaptureBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);    m_wndCaptureBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);    m_dockingbars.AddTail(&m_wndCaptureBar);    m_wndNavigationBar.Create(this, AFX_IDW_DOCKBAR_LEFT);    m_wndNavigationBar.SetBarStyle(m_wndNavigationBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);    m_wndNavigationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);    m_dockingbars.AddTail(&m_wndNavigationBar);    m_wndShaderEditorBar.Create(this, AFX_IDW_DOCKBAR_TOP);    m_wndShaderEditorBar.SetBarStyle(m_wndShaderEditorBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);    m_wndShaderEditorBar.EnableDocking(CBRS_ALIGN_ANY);    m_dockingbars.AddTail(&m_wndShaderEditorBar);    // Hide all dockable bars by default    POSITION pos = m_dockingbars.GetHeadPosition();    while (pos) {        m_dockingbars.GetNext(pos)->ShowWindow(SW_HIDE);    }    m_fileDropTarget.Register(this);    const CAppSettings& s = AfxGetAppSettings();    // Load the controls    m_nCS = s.nCS;    ShowControls(m_nCS);//是否在最前?    SetAlwaysOnTop(s.iOnTop);//显示系统托盘图标    ShowTrayIcon(s.fTrayIcon);//焦点    SetFocus();//创建CGraphThread类的线程    m_pGraphThread = (CGraphThread*)AfxBeginThread(RUNTIME_CLASS(CGraphThread));//设置。。    if (m_pGraphThread) {        m_pGraphThread->SetMainFrame(this);    }    if (s.nCmdlnWebServerPort != 0) {        if (s.nCmdlnWebServerPort > 0) {            StartWebServer(s.nCmdlnWebServerPort);        } else if (s.fEnableWebServer) {            StartWebServer(s.nWebServerPort);        }    }    // Casimir666 : reload Shaders    {        CString strList = s.strShaderList;        CString strRes;        int curPos = 0;        strRes = strList.Tokenize(_T("|"), curPos);        while (!strRes.IsEmpty()) {            m_shaderlabels.AddTail(strRes);            strRes = strList.Tokenize(_T("|"), curPos);        }    }    {        CString strList = s.strShaderListScreenSpace;        CString strRes;        int curPos = 0;        strRes = strList.Tokenize(_T("|"), curPos);        while (!strRes.IsEmpty()) {            m_shaderlabelsScreenSpace.AddTail(strRes);            strRes = strList.Tokenize(_T("|"), curPos);        }    }    m_bToggleShader = s.fToggleShader;    m_bToggleShaderScreenSpace = s.fToggleShaderScreenSpace;//标题    m_strTitle.LoadString(IDR_MAINFRAME);#ifdef MPCHC_LITE    m_strTitle += _T(" Lite");#endif//设置窗口标题    SetWindowText(m_strTitle);    m_Lcd.SetMediaTitle(LPCTSTR(m_strTitle));    WTSRegisterSessionNotification();    if (s.bNotifySkype) {        m_pSkypeMoodMsgHandler.Attach(DEBUG_NEW SkypeMoodMsgHandler());        m_pSkypeMoodMsgHandler->Connect(m_hWnd);    }    return 0;}</span>

在mpc-hc关闭的时候,会调用OnDestroy():

<span style="font-family:Arial;font-size:12px;">//关闭的时候加载void CMainFrame::OnDestroy(){    WTSUnRegisterSessionNotification();//关闭系统托盘图标    ShowTrayIcon(false);    m_fileDropTarget.Revoke();//线程还在运行的话    if (m_pGraphThread) {        CAMMsgEvent e;//退出        m_pGraphThread->PostThreadMessage(CGraphThread::TM_EXIT, 0, (LPARAM)&e);        if (!e.Wait(5000)) {            TRACE(_T("ERROR: Must call TerminateThread() on CMainFrame::m_pGraphThread->m_hThread\n"));            TerminateThread(m_pGraphThread->m_hThread, (DWORD) - 1);        }    }    if (m_pFullscreenWnd) {        if (m_pFullscreenWnd->IsWindow()) {            m_pFullscreenWnd->DestroyWindow();        }        delete m_pFullscreenWnd;    }    __super::OnDestroy();}</span>

在关闭一个媒体的时候,会调用OnClose():

//关闭的时候加载void CMainFrame::OnClose(){//获取设置    CAppSettings& s = AfxGetAppSettings();    // Casimir666 : save shaders list    {        POSITION pos;        CString strList = "";        pos = m_shaderlabels.GetHeadPosition();        while (pos) {            strList += m_shaderlabels.GetAt(pos) + "|";            m_dockingbars.GetNext(pos);        }        s.strShaderList = strList;    }    {        POSITION pos;        CString  strList = "";        pos = m_shaderlabelsScreenSpace.GetHeadPosition();        while (pos) {            strList += m_shaderlabelsScreenSpace.GetAt(pos) + "|";            m_dockingbars.GetNext(pos);        }        s.strShaderListScreenSpace = strList;    }    s.fToggleShader = m_bToggleShader;    s.fToggleShaderScreenSpace = m_bToggleShaderScreenSpace;    s.dZoomX = m_ZoomX;    s.dZoomY = m_ZoomY;//存储播放列表    m_wndPlaylistBar.SavePlaylist();//存储控制条    SaveControlBars();    ShowWindow(SW_HIDE);//关闭媒体(非private)    CloseMedia();    s.WinLircClient.DisConnect();    s.UIceClient.DisConnect();    SendAPICommand(CMD_DISCONNECT, L"\0");  // according to CMD_NOTIFYENDOFSTREAM (ctrl+f it here), you're not supposed to send NULL here    //调用父类onclose__super::OnClose();}

同时还有一个定时器函数OnTimer()。根据不同的nIDEvent做不同的处理操作。

//定时刷新的操作void CMainFrame::OnTimer(UINT_PTR nIDEvent){    switch (nIDEvent) {//当前播放到的位置        case TIMER_STREAMPOSPOLLER:            if (m_iMediaLoadState == MLS_LOADED) {                REFERENCE_TIME rtNow = 0, rtDur = 0;//播放方式是文件的时候(还可以是DVD或者摄像头)                if (GetPlaybackMode() == PM_FILE) {//当前位置                    m_pMS->GetCurrentPosition(&rtNow);//时常                    m_pMS->GetDuration(&rtDur);                    // Casimir666 : autosave subtitle sync after play                    if ((m_nCurSubtitle >= 0) && (m_rtCurSubPos != rtNow)) {                        if (m_lSubtitleShift != 0) {                            if (m_wndSubresyncBar.SaveToDisk()) {                                m_OSD.DisplayMessage(OSD_TOPLEFT, ResStr(IDS_AG_SUBTITLES_SAVED), 500);                            } else {                                m_OSD.DisplayMessage(OSD_TOPLEFT, ResStr(IDS_MAINFRM_4));                            }                        }                        m_nCurSubtitle   = -1;                        m_lSubtitleShift = 0;                    }                    if (!m_fEndOfStream) {                        CAppSettings& s = AfxGetAppSettings();                        if (m_bRememberFilePos) {                            FILE_POSITION* filePosition = s.filePositions.GetLatestEntry();                            if (filePosition) {                                filePosition->llPosition = rtNow;                                LARGE_INTEGER time;                                QueryPerformanceCounter(&time);                                LARGE_INTEGER freq;                                QueryPerformanceFrequency(&freq);                                if ((time.QuadPart - m_liLastSaveTime.QuadPart) >= 30 * freq.QuadPart) { // save every half of minute                                    m_liLastSaveTime = time;                                    s.filePositions.SaveLatestEntry();                                }                            }                        }                    }                    if (m_rtDurationOverride >= 0) {                        rtDur = m_rtDurationOverride;                    }//设置滑动条控件的参数(位置等。。。)                    g_bNoDuration = rtDur <= 0;                    m_wndSeekBar.Enable(rtDur > 0);                    m_wndSeekBar.SetRange(0, rtDur);                    m_wndSeekBar.SetPos(rtNow);                    m_OSD.SetRange(0, rtDur);                    m_OSD.SetPos(rtNow);                    m_Lcd.SetMediaRange(0, rtDur);                    m_Lcd.SetMediaPos(rtNow);                } else if (GetPlaybackMode() == PM_CAPTURE) {//如果是摄像头的话,就没有时长信息了                    m_pMS->GetCurrentPosition(&rtNow);                    if (m_fCapturing && m_wndCaptureBar.m_capdlg.m_pMux) {                        CComQIPtr<IMediaSeeking> pMuxMS = m_wndCaptureBar.m_capdlg.m_pMux;                        if (!pMuxMS || FAILED(pMuxMS->GetCurrentPosition(&rtNow))) {                            rtNow = 0;                        }                    }                    if (m_rtDurationOverride >= 0) {                        rtDur = m_rtDurationOverride;                    }                    g_bNoDuration = rtDur <= 0;                    m_wndSeekBar.Enable(false);                    m_wndSeekBar.SetRange(0, rtDur);                    m_wndSeekBar.SetPos(rtNow);                    m_OSD.SetRange(0, rtDur);                    m_OSD.SetPos(rtNow);                    m_Lcd.SetMediaRange(0, rtDur);                    m_Lcd.SetMediaPos(rtNow);                }                if (m_pCAP && GetPlaybackMode() != PM_FILE) {                    g_bExternalSubtitleTime = true;                    if (m_pDVDI) {                        DVD_PLAYBACK_LOCATION2 Location;                        if (m_pDVDI->GetCurrentLocation(&Location) == S_OK) {                            double fps = Location.TimeCodeFlags == DVD_TC_FLAG_25fps ? 25.0                                         : Location.TimeCodeFlags == DVD_TC_FLAG_30fps ? 30.0                                         : Location.TimeCodeFlags == DVD_TC_FLAG_DropFrame ? 29.97                                         : 25.0;                            REFERENCE_TIME rtTimeCode = HMSF2RT(Location.TimeCode, fps);                            m_pCAP->SetTime(rtTimeCode);                        } else {                            m_pCAP->SetTime(/*rtNow*/m_wndSeekBar.GetPos());                        }                    } else {                        // Set rtNow to support DVB subtitle                        m_pCAP->SetTime(rtNow);                    }                } else {                    g_bExternalSubtitleTime = false;                }            }            break;        case TIMER_STREAMPOSPOLLER2:            if (m_iMediaLoadState == MLS_LOADED) {                __int64 start, stop, pos;                m_wndSeekBar.GetRange(start, stop);                pos = m_wndSeekBar.GetPosReal();                GUID tf;                m_pMS->GetTimeFormat(&tf);                if (GetPlaybackMode() == PM_CAPTURE && !m_fCapturing) {                    CString str = ResStr(IDS_CAPTURE_LIVE);                    long lChannel = 0, lVivSub = 0, lAudSub = 0;                    if (m_pAMTuner                            && m_wndCaptureBar.m_capdlg.IsTunerActive()                            && SUCCEEDED(m_pAMTuner->get_Channel(&lChannel, &lVivSub, &lAudSub))) {                        CString ch;                        ch.Format(_T(" (ch%d)"), lChannel);                        str += ch;                    }                    m_wndStatusBar.SetStatusTimer(str);                } else {                    m_wndStatusBar.SetStatusTimer(pos, stop, !!m_wndSubresyncBar.IsWindowVisible(), &tf);                    if (m_bRemainingTime) {                        m_OSD.DisplayMessage(OSD_TOPLEFT, m_wndStatusBar.GetStatusTimer());                    }                }                m_wndSubresyncBar.SetTime(pos);                if (m_pCAP && GetMediaState() == State_Paused) {                    m_pCAP->Paint(false);                }            }            break;        case TIMER_FULLSCREENCONTROLBARHIDER: {            CPoint p;            GetCursorPos(&p);            CRect r;            GetWindowRect(r);            bool fCursorOutside = !r.PtInRect(p);            CWnd* pWnd = WindowFromPoint(p);            if (pWnd && (m_wndView == *pWnd || m_wndView.IsChild(pWnd) || fCursorOutside)) {                if (AfxGetAppSettings().nShowBarsWhenFullScreenTimeOut >= 0) {                    ShowControls(CS_NONE);                }            }        }        break;        case TIMER_FULLSCREENMOUSEHIDER: {            CPoint p;            GetCursorPos(&p);            CRect r;            GetWindowRect(r);            bool fCursorOutside = !r.PtInRect(p);            CWnd* pWnd = WindowFromPoint(p);            if (IsD3DFullScreenMode()) {                if (pWnd && !m_bInOptions && *pWnd == *m_pFullscreenWnd) {                    m_pFullscreenWnd->ShowCursor(false);                }                KillTimer(TIMER_FULLSCREENMOUSEHIDER);            } else {                if (pWnd && !m_bInOptions && (m_wndView == *pWnd || m_wndView.IsChild(pWnd) || fCursorOutside)) {                    m_fHideCursor = true;                    SetCursor(nullptr);                }            }        }        break;//统计量        case TIMER_STATS: {//接收端质量信息:抖动,抖动,视音频同步情况等。。。            if (m_pQP) {                CString rate;                rate.Format(_T("%.2f"), m_dSpeedRate);                rate = _T("(") + rate + _T("x)");//信息                CString info;                int val = 0;//平均帧率                m_pQP->get_AvgFrameRate(&val); // We hang here due to a lock that never gets released.                info.Format(_T("%d.%02d %s"), val / 100, val % 100, rate);                m_wndStatsBar.SetLine(ResStr(IDS_AG_FRAMERATE), info);                int avg, dev;//抖动                m_pQP->get_AvgSyncOffset(&avg);                m_pQP->get_DevSyncOffset(&dev);                info.Format(ResStr(IDS_STATSBAR_SYNC_OFFSET_FORMAT), avg, dev);                m_wndStatsBar.SetLine(ResStr(IDS_STATSBAR_SYNC_OFFSET), info);//掉帧                int drawn, dropped;                m_pQP->get_FramesDrawn(&drawn);                m_pQP->get_FramesDroppedInRenderer(&dropped);                info.Format(IDS_MAINFRM_6, drawn, dropped);                m_wndStatsBar.SetLine(ResStr(IDS_AG_FRAMES), info);//抖动                m_pQP->get_Jitter(&val);                info.Format(_T("%d ms"), val);                m_wndStatsBar.SetLine(ResStr(IDS_STATSBAR_JITTER), info);            }//缓存信息            if (m_pBI) {                CAtlList<CString> sl;//获取数量                for (int i = 0, j = m_pBI->GetCount(); i < j; i++) {                    int samples, size;//获取缓存状态                    if (S_OK == m_pBI->GetStatus(i, samples, size)) {                        CString str;                        str.Format(_T("[%d]: %03d/%d KB"), i, samples, size / 1024);                        sl.AddTail(str);                    }                }                if (!sl.IsEmpty()) {                    CString str;                    str.Format(_T("%s (p%u)"), Implode(sl, ' '), m_pBI->GetPriority());                    m_wndStatsBar.SetLine(ResStr(IDS_AG_BUFFERS), str);                }            }//比特率信息            CInterfaceList<IBitRateInfo> pBRIs;            BeginEnumFilters(m_pGB, pEF, pBF) {                BeginEnumPins(pBF, pEP, pPin) {                    if (CComQIPtr<IBitRateInfo> pBRI = pPin) {                        pBRIs.AddTail(pBRI);                    }                }                EndEnumPins;                if (!pBRIs.IsEmpty()) {                    CAtlList<CString> sl;                    POSITION pos = pBRIs.GetHeadPosition();                    for (int i = 0; pos; i++) {//比特率接口                        IBitRateInfo* pBRI = pBRIs.GetNext(pos);//当前比特率                        DWORD cur = pBRI->GetCurrentBitRate() / 1000;//平均比特率                        DWORD avg = pBRI->GetAverageBitRate() / 1000;                        if (avg == 0) {                            continue;                        }//添加到字符串                        CString str;                        if (cur != avg) {                            str.Format(_T("[%d]: %u/%u Kb/s"), i, avg, cur);                        } else {                            str.Format(_T("[%d]: %u Kb/s"), i, avg);                        }//加入                        sl.AddTail(str);                    }                    if (!sl.IsEmpty()) {                        m_wndStatsBar.SetLine(ResStr(IDS_STATSBAR_BITRATE), Implode(sl, ' ') + ResStr(IDS_STATSBAR_BITRATE_AVG_CUR));                    }                    break;                }            }            EndEnumFilters;            if (GetPlaybackMode() == PM_DVD) { // we also use this timer to update the info panel for DVD playback                ULONG ulAvailable, ulCurrent;                // Location                CString Location('-');                DVD_PLAYBACK_LOCATION2 loc;                ULONG ulNumOfVolumes, ulVolume;                DVD_DISC_SIDE Side;                ULONG ulNumOfTitles;                ULONG ulNumOfChapters;                if (SUCCEEDED(m_pDVDI->GetCurrentLocation(&loc))                        && SUCCEEDED(m_pDVDI->GetNumberOfChapters(loc.TitleNum, &ulNumOfChapters))                        && SUCCEEDED(m_pDVDI->GetDVDVolumeInfo(&ulNumOfVolumes, &ulVolume, &Side, &ulNumOfTitles))) {                    Location.Format(IDS_MAINFRM_9,                                    ulVolume, ulNumOfVolumes,                                    loc.TitleNum, ulNumOfTitles,                                    loc.ChapterNum, ulNumOfChapters);                    ULONG tsec = (loc.TimeCode.bHours * 3600)                                 + (loc.TimeCode.bMinutes * 60)                                 + (loc.TimeCode.bSeconds);                    /* This might not always work, such as on resume */                    if (loc.ChapterNum != m_lCurrentChapter) {                        m_lCurrentChapter = loc.ChapterNum;                        m_lChapterStartTime = tsec;                    } else {                        /* If a resume point was used, and the user chapter jumps,                        then it might do some funky time jumping.  Try to 'fix' the                        chapter start time if this happens */                        if (m_lChapterStartTime > tsec) {                            m_lChapterStartTime = tsec;                        }                    }                }                m_wndInfoBar.SetLine(ResStr(IDS_INFOBAR_LOCATION), Location);                // Video                CString Video('-');                DVD_VideoAttributes VATR;                if (SUCCEEDED(m_pDVDI->GetCurrentAngle(&ulAvailable, &ulCurrent))                        && SUCCEEDED(m_pDVDI->GetCurrentVideoAttributes(&VATR))) {                    Video.Format(IDS_MAINFRM_10,                                 ulCurrent, ulAvailable,                                 VATR.ulSourceResolutionX, VATR.ulSourceResolutionY, VATR.ulFrameRate,                                 VATR.ulAspectX, VATR.ulAspectY);                }                m_wndInfoBar.SetLine(ResStr(IDS_INFOBAR_VIDEO), Video);                // Audio                CString Audio('-');                DVD_AudioAttributes AATR;                if (SUCCEEDED(m_pDVDI->GetCurrentAudio(&ulAvailable, &ulCurrent))                        && SUCCEEDED(m_pDVDI->GetAudioAttributes(ulCurrent, &AATR))) {                    CString lang;                    if (AATR.Language) {                        int len = GetLocaleInfo(AATR.Language, LOCALE_SENGLANGUAGE, lang.GetBuffer(64), 64);                        lang.ReleaseBufferSetLength(max(len - 1, 0));                    } else {                        lang.Format(IDS_AG_UNKNOWN, ulCurrent + 1);                    }                    switch (AATR.LanguageExtension) {                        case DVD_AUD_EXT_NotSpecified:                        default:                            break;                        case DVD_AUD_EXT_Captions:                            lang += _T(" (Captions)");                            break;                        case DVD_AUD_EXT_VisuallyImpaired:                            lang += _T(" (Visually Impaired)");                            break;                        case DVD_AUD_EXT_DirectorComments1:                            lang += _T(" (Director Comments 1)");                            break;                        case DVD_AUD_EXT_DirectorComments2:                            lang += _T(" (Director Comments 2)");                            break;                    }                    CString format = GetDVDAudioFormatName(AATR);                    Audio.Format(IDS_MAINFRM_11,                                 lang,                                 format,                                 AATR.dwFrequency,                                 AATR.bQuantization,                                 AATR.bNumberOfChannels,                                 (AATR.bNumberOfChannels > 1 ? ResStr(IDS_MAINFRM_13) : ResStr(IDS_MAINFRM_12)));                    m_wndStatusBar.SetStatusBitmap(                        AATR.bNumberOfChannels == 1 ? IDB_AUDIOTYPE_MONO                        : AATR.bNumberOfChannels >= 2 ? IDB_AUDIOTYPE_STEREO                        : IDB_AUDIOTYPE_NOAUDIO);                }                m_wndInfoBar.SetLine(ResStr(IDS_INFOBAR_AUDIO), Audio);                // Subtitles                CString Subtitles('-');                BOOL bIsDisabled;                DVD_SubpictureAttributes SATR;                if (SUCCEEDED(m_pDVDI->GetCurrentSubpicture(&ulAvailable, &ulCurrent, &bIsDisabled))                        && SUCCEEDED(m_pDVDI->GetSubpictureAttributes(ulCurrent, &SATR))) {                    CString lang;                    int len = GetLocaleInfo(SATR.Language, LOCALE_SENGLANGUAGE, lang.GetBuffer(64), 64);                    lang.ReleaseBufferSetLength(max(len - 1, 0));                    switch (SATR.LanguageExtension) {                        case DVD_SP_EXT_NotSpecified:                        default:                            break;                        case DVD_SP_EXT_Caption_Normal:                            lang += _T("");                            break;                        case DVD_SP_EXT_Caption_Big:                            lang += _T(" (Big)");                            break;                        case DVD_SP_EXT_Caption_Children:                            lang += _T(" (Children)");                            break;                        case DVD_SP_EXT_CC_Normal:                            lang += _T(" (CC)");                            break;                        case DVD_SP_EXT_CC_Big:                            lang += _T(" (CC Big)");                            break;                        case DVD_SP_EXT_CC_Children:                            lang += _T(" (CC Children)");                            break;                        case DVD_SP_EXT_Forced:                            lang += _T(" (Forced)");                            break;                        case DVD_SP_EXT_DirectorComments_Normal:                            lang += _T(" (Director Comments)");                            break;                        case DVD_SP_EXT_DirectorComments_Big:                            lang += _T(" (Director Comments, Big)");                            break;                        case DVD_SP_EXT_DirectorComments_Children:                            lang += _T(" (Director Comments, Children)");                            break;                    }                    if (bIsDisabled) {                        lang = _T("-");                    }                    Subtitles.Format(_T("%s"),                                     lang);                }                m_wndInfoBar.SetLine(ResStr(IDS_INFOBAR_SUBTITLES), Subtitles);            } else if (GetPlaybackMode() == PM_CAPTURE && AfxGetAppSettings().iDefaultCaptureDevice == 1) {                CComQIPtr<IBDATuner> pTun = m_pGB;                BOOLEAN bPresent;                BOOLEAN bLocked;                LONG lDbStrength;                LONG lPercentQuality;                CString Signal;                if (SUCCEEDED(pTun->GetStats(bPresent, bLocked, lDbStrength, lPercentQuality)) && bPresent) {                    Signal.Format(ResStr(IDS_STATSBAR_SIGNAL_FORMAT), (int)lDbStrength, lPercentQuality);                    m_wndStatsBar.SetLine(ResStr(IDS_STATSBAR_SIGNAL), Signal);                }            } else if (GetPlaybackMode() == PM_FILE) {                UpdateChapterInInfoBar();            }            if (GetMediaState() == State_Running && !m_fAudioOnly) {                BOOL fActive = FALSE;                if (SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0,       &fActive, 0)) {                    SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE,   nullptr,     SPIF_SENDWININICHANGE); // this might not be needed at all...                    SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, fActive, nullptr,     SPIF_SENDWININICHANGE);                }                fActive = FALSE;                if (SystemParametersInfo(SPI_GETPOWEROFFACTIVE, 0,       &fActive, 0)) {                    SystemParametersInfo(SPI_SETPOWEROFFACTIVE, FALSE,   nullptr,     SPIF_SENDWININICHANGE); // this might not be needed at all...                    SystemParametersInfo(SPI_SETPOWEROFFACTIVE, fActive, nullptr,     SPIF_SENDWININICHANGE);                }                // prevent screensaver activate, monitor sleep/turn off after playback                SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);            }        }        break;        case TIMER_STATUSERASER: {            KillTimer(TIMER_STATUSERASER);            m_playingmsg.Empty();        }        break;        case TIMER_DVBINFO_UPDATER:            KillTimer(TIMER_DVBINFO_UPDATER);            ShowCurrentChannelInfo(false, false);            break;    }    __super::OnTimer(nIDEvent);}