www.pudn.com > src.rar > DirectVobSubFilter.cpp
#include "stdafx.h" #include "resource.h" #include#include #include "DirectVobSubUIDs.h" #include "DirectVobSubFilter.h" #include "DirectVobSubInputPin.h" #include "DirectVobSubOutputPin.h" #include "TextInputPin.h" #include "DirectVobSubPropPage.h" #include "SubtitleSource.h" #include "Main.h" #include "misc.h" #include "systray.h" #include "Main.h" /////////////////////////////////////////////////////////////////////////// #include "../vobsub/colorconv.cpp" //////////////////////////////////////////////////////////////////////////// // // Constructor // CDirectVobSubFilter::CDirectVobSubFilter(TCHAR* tszName, LPUNKNOWN punk, HRESULT* phr, const GUID& guid) : CTransformFilter(tszName, punk, guid), m_Allocator(this, phr), m_fUsingOwnAllocator(false) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); memset(&m_bihIn, 0, sizeof(BITMAPINFOHEADER)); memset(&m_bihSub, 0, sizeof(BITMAPINFOHEADER)); memset(&m_bihOut, 0, sizeof(BITMAPINFOHEADER)); m_mode = NONE; m_textidx = 0; m_file = NULL; m_fAudioReconnected = false; m_fMSMpeg4Fix = false; m_fDivxPlusFix = false; m_fVMRFilter = m_fVMRFilterActive = false; m_hdc = 0; m_hbm = 0; m_hfont = 0; m_fps = 25; /* ResX2 */ m_fResX2Active = false; m_pTempPicBuff = NULL; theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Hint"), _T("The first three are fixed, but you can add more up to ten entries.")); theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Path0"), _T(".")); theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Path1"), _T("c:\\subtitles")); theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Path2"), _T(".\\subtitles")); m_fLoading = false; HRESULT hr = S_OK; m_pTextInput.Add(new CTextInputPin(this, m_pLock, &hr)); m_fTextStreamWorkaroundDone = false; m_iLastSegmentPosted = -1; m_fThreadActive = m_fBreakBuffering = false; CAMThread::Create(); m_hSystrayThread = 0; m_tbid.hSystrayWnd = NULL; m_tbid.graph = NULL; m_tbid.fRunOnce = false; m_tbid.fShowIcon = (theApp.m_AppName.Find(_T("zplayer"), 0) < 0 || !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_ENABLEZPICON), 0)); m_frd.pFilter = this; m_frd.hThread = NULL; m_frd.hEndThreadEvent = CreateEvent(0, FALSE, FALSE, 0); m_frd.hRefreshEvent = CreateEvent(0, FALSE, FALSE, 0); DWORD tid; m_frd.hThread = CreateThread(0, 0, FileReloaderThreadProc, &m_frd, 0, &tid); SetThreadPriority(m_frd.hThread, THREAD_PRIORITY_LOWEST); /* if(SUCCEEDED(hr = DirectDrawCreateEx(NULL, (VOID**)&m_pDD, IID_IDirectDraw7, NULL))) { if(FAILED(m_pDD->SetCooperativeLevel(NULL, DDSCL_NORMAL))) m_pDD = NULL; } */ #ifdef DEBUG dwRegister = -1; #endif } CDirectVobSubFilter::~CDirectVobSubFilter() { CallWorker(CMD_EXIT); CAMThread::Close(); { CAutoLock lock(&m_fileLock); if(m_file) {delete m_file; m_file = NULL;} if(m_hfont) {DeleteObject(m_hfont); m_hfont = 0;} if(m_hbm) {DeleteObject(m_hbm); m_hbm = 0;} if(m_hdc) {DeleteObject(m_hdc); m_hdc = 0;} for(int i = 0, j = m_pTextInput.GetSize(); i < j; i++) delete m_pTextInput[i]; } if(m_frd.hThread) { SetThreadPriority(m_frd.hThread, THREAD_PRIORITY_NORMAL); SetEvent(m_frd.hEndThreadEvent); if(WaitForSingleObject(m_frd.hThread, 10000) != WAIT_OBJECT_0) { TRACE(_T("Whatever...")); TerminateThread(m_frd.hThread, 0); } m_frd.hThread = 0; m_frd.files.RemoveAll(); } CloseHandle(m_frd.hEndThreadEvent); CloseHandle(m_frd.hRefreshEvent); /* ResX2 */ if(m_pTempPicBuff) delete [] m_pTempPicBuff; } CUnknown* CDirectVobSubFilter::CreateInstance(LPUNKNOWN punk, HRESULT* phr) { CDirectVobSubFilter* pNewObject = new CDirectVobSubFilter(NAME("DirectVobSub"), punk, phr, CLSID_DirectVobSubFilter); if(pNewObject == NULL) *phr = E_OUTOFMEMORY; return pNewObject; } STDMETHODIMP CDirectVobSubFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv) { CheckPointer(ppv, E_POINTER); HRESULT hr; hr = (riid == IID_IDirectVobSub) ? GetInterface((IDirectVobSub *)this, ppv) :(riid == IID_IDirectVobSub2) ? GetInterface((IDirectVobSub2 *)this, ppv) :(riid == IID_ISpecifyPropertyPages) ? GetInterface((ISpecifyPropertyPages *)this, ppv) :(riid == IID_IPersistStream) ? GetInterface((IPersistStream *)this, ppv) :(riid == IID_IAMStreamSelect) ? GetInterface((IAMStreamSelect *)this, ppv) : CTransformFilter::NonDelegatingQueryInterface(riid, ppv); return hr; } HRESULT CDirectVobSubFilter::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName) { if(pGraph) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); if(!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SEENDIVXWARNING), 0)) { unsigned __int64 ver = GetFileVersion(_T("divx_c32.ax")); if(((ver >> 48)&0xffff) == 4 && ((ver >> 32)&0xffff) == 2) { DWORD dwVersion = GetVersion(); DWORD dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); DWORD dwWindowsMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); if(dwVersion < 0x80000000 && dwWindowsMajorVersion >= 5) { AfxMessageBox(IDS_DIVX_WARNING); theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SEENDIVXWARNING), 1); } } } } else { if(m_hSystrayThread) { SendMessage(m_tbid.hSystrayWnd, WM_CLOSE, 0, 0); if(WaitForSingleObject(m_hSystrayThread, 10000) != WAIT_OBJECT_0) { DbgLog((LOG_TRACE, 0, _T("CALL THE AMBULANCE!!!"))); TerminateThread(m_hSystrayThread, (DWORD)-1); } m_hSystrayThread = 0; } } return CTransformFilter::JoinFilterGraph(pGraph, pName); } DWORD g_dwRegister = 0; HRESULT CDirectVobSubFilter::StartStreaming() { m_fLoading = false; //// if(m_hfont) {DeleteObject(m_hfont); m_hfont = NULL;} if(m_hbm) {DeleteObject(m_hbm); m_hbm = NULL;} if(m_hdc) {DeleteDC(m_hdc); m_hdc = NULL;} if(!m_hdc) { m_hdc = CreateCompatibleDC(NULL); struct BM { BITMAPINFOHEADER bih; DWORD mask[3]; } b; memset(&b, 0, sizeof(b)); b.bih.biSize = sizeof(BITMAPINFOHEADER); b.bih.biWidth = m_bihSub.biWidth; b.bih.biHeight = -abs(m_bihSub.biHeight); b.bih.biBitCount = 32; b.bih.biPlanes = 1; b.bih.biCompression = BI_BITFIELDS; b.mask[0] = 0xFF0000; b.mask[1] = 0x00FF00; b.mask[2] = 0x0000FF; m_hbm = CreateDIBSection( m_hdc, (BITMAPINFO *)&b, DIB_RGB_COLORS, NULL, NULL, 0); BITMAP bm; GetObject(m_hbm, sizeof(bm), &bm); memsetd(bm.bmBits, 0xff000000, bm.bmHeight*bm.bmWidthBytes); } if(!m_hfont) { CAutoLock cAutolock(&m_propsLock); LOGFONT lf; memset(&lf, 0, sizeof(lf)); lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = OUT_CHARACTER_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = ANTIALIASED_QUALITY; HDC hdc = GetDC(NULL); lf.lfHeight = -MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72); ReleaseDC(NULL, hdc); lf.lfWeight = FW_BOLD; _tcscpy(lf.lfFaceName, _T("Arial")); m_hfont = CreateFontIndirect(&lf); } //// CallWorker(CMD_RESET); CAutoLock lock(&m_AccessLock); HookTextStreams(); m_iLastSegmentPosted = -1; m_tbid.fRunOnce = true; m_fDivxPlusFix = false; CComPtr pFilter; if(SUCCEEDED(m_pGraph->FindFilterByName(L"HPlus YUV Video Renderer", &pFilter))) m_fDivxPlusFix = true; /* ResX2 */ if(m_fResX2Active) {m_pTempPicBuff = new BYTE[4*m_bihIn.biWidth*m_bihIn.biHeight*4];} put_MediaFPS(m_fMediaFPSEnabled, m_MediaFPS); /* if(g_dwRegister == 0) AddToRot(m_pGraph, &g_dwRegister); */ return CTransformFilter::StartStreaming(); } HRESULT CDirectVobSubFilter::StopStreaming() { /* RemoveFromRot(g_dwRegister); g_dwRegister = 0; */ /* ResX2 */ if(m_pTempPicBuff) {delete [] m_pTempPicBuff; m_pTempPicBuff = NULL;} CallWorker(CMD_RESET); CAutoLock lock(&m_AccessLock); if(m_hfont) {DeleteObject(m_hfont); m_hfont = NULL;} if(m_hbm) {DeleteObject(m_hbm); m_hbm = NULL;} if(m_hdc) {DeleteDC(m_hdc); m_hdc = NULL;} return CTransformFilter::StopStreaming(); } HRESULT CDirectVobSubFilter::CheckConnect(PIN_DIRECTION dir, IPin* pPin) { if(dir == PINDIR_INPUT) { } else if(dir == PINDIR_OUTPUT) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); if(!!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_VMRZOOMENABLED), 0)) { CComQIPtr pVMRFC = FindVMRFilterFromPin(pPin); if(pVMRFC && pVMRFC->SetNumberOfStreams(1) == S_OK) m_fVMRFilterActive = true; } } return CTransformFilter::CheckConnect(dir, pPin); } HRESULT CDirectVobSubFilter::CompleteConnect(PIN_DIRECTION dir, IPin* pReceivePin) { if(dir == PINDIR_INPUT) { CComPtr pFilter; // needed when we have a decoder with a version number of 3.x if(SUCCEEDED(m_pGraph->FindFilterByName(L"DivX MPEG-4 DVD Video Decompressor ", &pFilter)) || SUCCEEDED(m_pGraph->FindFilterByName(L"Microcrap MPEG-4 Video Decompressor", &pFilter)) || (SUCCEEDED(m_pGraph->FindFilterByName(L"Microsoft MPEG-4 Video Decompressor", &pFilter)) && (GetFileVersion(_T("mpg4ds32.ax")) >> 48) <= 3)) { m_fMSMpeg4Fix = true; } for(int i = 0, j = m_pTextInput.GetSize(), k = 0; i < j; i++) { if(m_pTextInput[i]->IsConnected()) k++; } if(i == k) { HRESULT hr; m_pTextInput.Add(new CTextInputPin(this, m_pLock, &hr)); } } else if(dir == PINDIR_OUTPUT) { if(!m_hSystrayThread) { m_tbid.graph = m_pGraph; m_tbid.dvs = static_cast (this); DWORD tid; m_hSystrayThread = CreateThread(0, 0, SystrayThreadProc, &m_tbid, 0, &tid); } int w = m_bihIn.biWidth, h = m_bihIn.biHeight; m_fResX2Active = AdjustFrameSize(w, h); m_bihSub = m_bihIn; m_bihSub.biWidth = w; m_bihSub.biHeight = h; m_bihSub.biSizeImage = m_bihSub.biWidth*m_bihSub.biHeight*4; CallWorker(CMD_INIT); InitRTSStreams(false); CComQIPtr pVMRFC = FindVMRFilterFromPin(pReceivePin); if(pVMRFC) m_fVMRFilter = true; else m_fVMRFilterActive = false; } return CTransformFilter::CompleteConnect(dir, pReceivePin); } HRESULT CDirectVobSubFilter::BreakConnect(PIN_DIRECTION dir) { if(dir == PINDIR_INPUT) { } else if(dir == PINDIR_OUTPUT) { m_fVMRFilter = m_fVMRFilterActive = false; } return CTransformFilter::BreakConnect(dir); } CBasePin* CDirectVobSubFilter::GetPin(int n) { if(n >= 0 && n < 2) { if(m_pInput == NULL) { HRESULT hr = S_OK; m_pInput = new CDirectVobSubInputPin(this, &hr); ASSERT(SUCCEEDED(hr)); if(m_pInput == NULL) return NULL; m_pOutput = new CDirectVobSubOutputPin(this, &hr); ASSERT(SUCCEEDED(hr)); if(m_pOutput == NULL) { delete m_pInput; m_pInput = NULL; } } return (n == 0) ? (CBasePin*)m_pInput : (n == 1) ? (CBasePin*)m_pOutput : NULL; } n -= 2; if(n >= 0 && n < m_pTextInput.GetSize()) { return m_pTextInput[n]; } n -= m_pTextInput.GetSize(); return NULL; } int CDirectVobSubFilter::GetPinCount() { return(2 + m_pTextInput.GetSize()); } int CDirectVobSubFilter::CalcCurrentTime() { int t = m_pSubClock ? (int)(m_pSubClock->GetTime()/10000) : m_tPrev.Millisecs(); return MulDiv(t - m_SubtitleDelay, m_SubtitleSpeedMul, m_SubtitleSpeedDiv); } void StretchPut(BITMAP& bm, CRect dstrect, CVobSubImage& src, int fSmooth); void CDirectVobSubFilter::AddVobSub(SubImage* img, int start, int stop) { BITMAP bm; GetObject(img->hbm, sizeof(BITMAP), &bm); if(m_fPolygonize) { uint color = m_file->m_img.ClearBorders(); CRenderedTextSubtitle rts; rts.m_fSSA = true; rts.m_dstScreenSize = m_file->GetSize(); #ifdef UNICODE rts.CreateDefaultStyle(DEFAULT_CHARSET, true); #else rts.CreateDefaultStyle(DEFAULT_CHARSET, false); #endif STSStyle defstyle; rts.GetDefaultStyle(defstyle); defstyle.colors[0] = m_TextColor;//((color>>16)&0xff)|((color>>8)&0xff00)|((color<<16)&0xff0000) defstyle.outlineWidth = m_fOutline?2:0; defstyle.shadowDepth = m_fShadow?2:0; HDC hdc = ::GetDC(0); int lfHeight = -MulDiv(m_lf.lfHeight, 72, GetDeviceCaps(hdc, LOGPIXELSY)); ReleaseDC(0, hdc); defstyle.fontScaleX = defstyle.fontScaleY = 100*lfHeight/defstyle.fontSize; defstyle.scrAlignment = 5; rts.SetDefaultStyle(defstyle); CString str; if(!m_file->m_img.Polygonize(str)) return; CString pos; if(m_fOverridePlacement) { pos.Format(_T("{\\an2\\pos(%d,%d)}"), MulDiv(m_file->GetSize().cx, m_PlacementXperc, 100), MulDiv(m_file->GetSize().cy, m_PlacementYperc, 100)); } else { pos.Format(_T("{\\pos(%d,%d)}"), m_file->m_img.rect.CenterPoint().x, m_file->m_img.rect.CenterPoint().y); } rts.Add(_T(" "), start, stop, pos + str, _T("Default"), _T(""), _T("")); img->Clear(0xff000000); rts.Render(bm, (start+stop)/2, m_fps); } else { m_file->SetAlignment(m_fOverridePlacement, m_PlacementXperc, m_PlacementYperc, 1, 1); CRect r; m_file->GetDestrect(r, bm.bmWidth, bm.bmHeight); img->Clear(0xff000000); StretchPut(bm, r, m_file->m_img, m_file->m_fSmooth); } img->start = start; img->stop = stop; if(m_bihIn.biCompression > 3) img->ConvertToYUY2(); m_sic.MarkNextAsOccupied(); } void CDirectVobSubFilter::AddText(SubImage* img, int start, int stop, int PTS) { CRenderedTextSubtitle& rts = m_textlangs[m_textidx]; if(m_fAdvancedRenderer) { if(!m_fDoPreBuffering) { m_sic.RemoveAll(); img = m_sic.GetNextFree(); ASSERT(img); } BITMAP bm; GetObject(img->hbm, sizeof(bm), &bm); img->Clear(0xff000000); if(m_fDoPreBuffering) { if(!rts.Render(bm, (start+stop)>>1, m_fps)) return; img->start = start; img->stop = stop; } else { if(!rts.Render(bm, PTS, m_fps)) return; img->start = PTS; img->stop = PTS + LOOKAHEADSTEPSIZE; } } else { int i = rts.SearchSub((start+stop)>>1, m_fps); if(i < 0) return; HDC hdc = m_sic.GetDC(); HFONT hfont = m_sic.GetFont(); #ifndef UNICODE // Under Win9x we have to select the font into the screen's DC first to have antialiasing, argh... HDC hdcScreen = GetDC(NULL); HFONT hOldScreenFont = (HFONT)SelectObject(hdcScreen, hfont); SelectObject(hdcScreen, hOldScreenFont); ReleaseDC(NULL, hdcScreen); #endif BITMAP bm; GetObject(img->hbm, sizeof(BITMAP), &bm); HANDLE hbmOld = SelectObject(hdc, img->hbm); HANDLE hfontOld = SelectObject(hdc, hfont); CString str = rts.GetStr(i); // int border = min(min(30, bm.bmWidth/3), bm.bmHeight/3); // CRect r = CRect(border, border, bm.bmWidth - border, bm.bmHeight - border), r2 = r; CRect r = CRect(0, 0, bm.bmWidth, bm.bmHeight), r2 = r; DrawText(hdc, str, str.GetLength(), &r2, DT_CALCRECT|DT_EXTERNALLEADING|DT_NOPREFIX|DT_WORDBREAK); r2 -= r2.CenterPoint(); if(!m_fOverridePlacement) { int h = abs((int)m_bihIn.biHeight); CPoint p = r.CenterPoint(); r2 += h < bm.bmHeight ? CPoint(p.x, (bm.bmHeight*3 + h) / 4) : CPoint(p.x, (r.bottom*4 + p.y) / 5); if(r2.bottom > bm.bmHeight) r2 -= CPoint(0, r2.bottom - bm.bmHeight + 10); } else { r2 += CPoint(MulDiv(bm.bmWidth, m_PlacementXperc, 100), MulDiv(bm.bmHeight, m_PlacementYperc, 100)); } CRect rr = r2 + CRect(3,3,3,3); img->Clear(0xff000000); SetMapMode(hdc, MM_TEXT); SetBkMode(hdc, TRANSPARENT); UINT flags = DT_CENTER|DT_VCENTER|DT_NOPREFIX|DT_WORDBREAK; int CharSet = rts.GetCharSet(i); if(CharSet == HEBREW_CHARSET || CharSet == ARABIC_CHARSET) flags |= DT_RTLREADING; static const char outline[][2] = { {-1,-2},{ 0,-2},{+1,-2}, {-2,-1},{-1,-1},{ 0,-1},{+1,-1},{+2,-1}, {-2, 0},{-1, 0}, {+1, 0},{+2, 0}, {-2,+1},{-1,+1},{ 0,+1},{+1,+1},{+2,+1}, {-1,+2},{ 0,+2},{+1,+2}, }; { if(m_fOutline) { SetTextColor(hdc, 0); for(int i = 0; i < 20; i++) { CRect r3 = r2 + CPoint(outline[i][0], outline[i][1]); DrawText(hdc, str, str.GetLength(), &r3, flags); } } if(m_fShadow) { SetTextColor(hdc, 0); CRect r3 = r2 + CPoint(2, 2); if(m_fOutline) r3 += CPoint(1, 1); DrawText(hdc, str, str.GetLength(), &r3, flags); } SetTextColor(hdc, m_TextColor); } DrawText(hdc, str, str.GetLength(), &r2, flags); r2.InflateRect(2, 2); r &= r2; SelectObject(hdc, hfontOld); SelectObject(hdc, hbmOld); GdiFlush(); img->start = start; img->stop = stop; } if(m_bihIn.biCompression > 3) img->ConvertToYUY2(); m_sic.MarkNextAsOccupied(); } SubImage* CDirectVobSubFilter::GetSubImage() { if(m_fHideSubtitles) return(NULL); int PTS = CalcCurrentTime(); SubImage* ret = NULL; m_sic.Update(PTS); if(m_fDoPreBuffering) { if(!m_fThreadActive) CallWorker(CMD_SETPTS); } ret = m_sic.LookupSubImage(PTS); if(!m_fDoPreBuffering) { m_sic.MarkAsStart(PTS); if(!ret) { CAutoLock cAutolock(&m_fileLock); if(m_mode == VOBSUB && m_file != NULL) { int i = m_file->GetFrameIdxByTimeStamp(PTS); if(i >= 0 && m_file->GetFrame(i) && (ret = m_sic.GetNextFree()) != NULL) { int start = m_file->m_img.nTimeStamp; int stop = m_file->m_img.nTimeStamp + m_file->m_img.nDisplayTime; if(PTS < stop) AddVobSub(ret, start, stop); } } else if(m_mode == TEXT && m_textidx >= 0 && m_textidx < m_textlangs.GetSize()) { CRenderedTextSubtitle& rts = m_textlangs[m_textidx]; int segment; const STSSegment* s = rts.SearchSubs(PTS, m_fps, &segment); if(s && (ret = m_sic.GetNextFree()) != NULL) { int start = rts.TranslateSegmentStart(segment, m_fps); int stop = rts.TranslateSegmentEnd(segment, m_fps); AddText(ret, start, stop, PTS); } } ret = m_sic.LookupSubImage(PTS); } } return(ret); } void CDirectVobSubFilter::Post_EC_OLE_EVENT() { if(m_mode != TEXT || m_textidx < 0 || m_textidx >= m_textlangs.GetSize()) return; CComQIPtr pMES = m_pGraph; if(!pMES) return; CRenderedTextSubtitle& rts = m_textlangs[m_textidx]; // CAutoLock cAutoLock(&rts.m_csLock); CComBSTR bstr1("Text"), bstr2; int segment; const STSSegment* s = rts.SearchSubs(CalcCurrentTime(), m_fps, &segment); CString str; if(!s && m_iLastSegmentPosted >= 0) { bstr2 = " "; CComQIPtr pMES = m_pGraph; if(pMES) pMES->Notify(EC_OLE_EVENT, (LONG_PTR)bstr1.Detach(), (LONG_PTR)bstr2.Detach()); m_iLastSegmentPosted = -1; } else if(s && m_iLastSegmentPosted != segment) { int start = rts.TranslateSegmentStart(segment, m_fps); int stop = rts.TranslateSegmentEnd(segment, m_fps); for(int i = 0, j = s->subs.GetSize(); i < j; i++) { str = rts.GetMBCSStr(s->subs[i]); #ifdef UNICODE char* buff = new char[str.GetLength()+2]; for(int k = 0; k < str.GetLength(); k++) buff[k] = (char)str[k]; if(i 0) { CComQIPtr pMES = m_pGraph; if(pMES) pMES->Notify(EC_OLE_EVENT, (LONG_PTR)bstr1.Detach(), (LONG_PTR)bstr2.Detach()); } m_iLastSegmentPosted = segment; } } HRESULT CDirectVobSubFilter::Receive(IMediaSample* pSample) { HRESULT hr = CTransformFilter::Receive(pSample); // It could have been placed into Transform(..), but if we don't // deliver the sample before sending EC_OLE_EVENT, the text // will appear one frame earlier (I guess because the renderer // is double/triple-buffering the picture) Post_EC_OLE_EVENT(); return hr; } HRESULT CDirectVobSubFilter::Transform(IMediaSample* pIn, IMediaSample* pOut) { #ifdef DEBUG clock_t startt = clock(); #endif CRefTime tStart, tEnd; pIn->GetTime(&tStart.m_time, &tEnd.m_time); m_tPrev = m_tOff + m_pInput->CurrentRate()*tStart; m_fps = (10000000.0/(tEnd-tStart)) / m_pInput->CurrentRate(); bool fSkipFrame = false; { CMediaType* pmt = NULL; if(S_OK == pOut->GetMediaType((AM_MEDIA_TYPE**)&pmt) && pmt) { CMediaType mt(*pmt); DeleteMediaType(pmt); pmt = NULL; if(mt != m_pOutput->CurrentMediaType()) { m_pOutput->SetMediaType(&mt); fSkipFrame = true; } } } if(!fSkipFrame) { CMediaType* pmt = NULL; if(S_OK == pIn->GetMediaType((AM_MEDIA_TYPE**)&pmt) && pmt) { CMediaType mt(*pmt); DeleteMediaType(pmt); pmt = NULL; if(mt != m_pInput->CurrentMediaType()) { // this is only for sending aspect ratio or palette changes // downstream // // other format changes would already work with the VMR, but // not with the older renderers, so we must not change anything // serious here which would affect the color space or the // picture buffer size (we can be safe about these, by default // the input pin is rejecting ReceiveConnection when running) m_pInput->SetMediaType(&mt); ConvertMediaTypeInputToOutput(&mt); pOut->SetMediaType(&mt); } } } BYTE* pInPtr; pIn->GetPointer(&pInPtr); BYTE* pOutPtr; pOut->GetPointer(&pOutPtr); if(fSkipFrame || m_bihSub.biHeight != abs(m_bihOut.biHeight)) { DbgLog((LOG_TRACE, 0, _T("Transform: %dx%d %dbpp 0x%08x"), m_bihOut.biWidth, m_bihOut.biHeight, m_bihOut.biBitCount, m_bihOut.biCompression)); if(m_bihOut.biCompression == mmioFOURCC('Y', 'V', '1', '2')) { uint size = m_bihOut.biWidth*abs(m_bihOut.biHeight); memset(pOutPtr, 0x10, size); memset(pOutPtr+size, 0x80, size>>1); } else { uint black = m_bihOut.biCompression <= 3 ? 0 : 0x80108010; memsetd(pOutPtr, black, m_bihOut.biSizeImage); } } else { SubImage* img = GetSubImage(); if(m_bihOut.biCompression == mmioFOURCC('Y', 'V', '1', '2')) { HRESULT hr = CopyYV12(pOutPtr, pInPtr, img); if(FAILED(hr)) return hr; } else { HRESULT hr = Copy(pOutPtr, pInPtr, img); if(FAILED(hr)) return hr; } PrintMessages(pOutPtr); } #ifdef DEBUG clock_t dtt = clock() - startt; if(dtt > 20) DbgLog((LOG_TRACE, 0, _T("Transform: %d"), dtt)); #endif return NOERROR; } HRESULT CDirectVobSubFilter::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { // CallWorker(CMD_BREAK); m_fBreakBuffering = true; m_sic.RemoveAll(); m_tOff = m_tPrev = tStart; Post_EC_OLE_EVENT(); return CTransformFilter::NewSegment(tStart, tStop, dRate); } HRESULT CDirectVobSubFilter::CheckInputType(const CMediaType* mtIn) { if(CheckOutputType(mtIn)) return VFW_E_TYPE_NOT_ACCEPTED; BITMAPINFOHEADER bih; ExtractBIH(mtIn, &bih); return bih.biHeight > 0 ? NOERROR : VFW_E_TYPE_NOT_ACCEPTED; } HRESULT CDirectVobSubFilter::CheckOutputType(const CMediaType* mtOut) { if(mtOut->majortype != MEDIATYPE_Video || !(mtOut->formattype == FORMAT_VideoInfo || mtOut->formattype == FORMAT_VideoInfo2)) return E_FAIL; return (mtOut->subtype == MEDIASUBTYPE_YUY2 || mtOut->subtype == MEDIASUBTYPE_YV12 || mtOut->subtype == MEDIASUBTYPE_RGB555 || mtOut->subtype == MEDIASUBTYPE_RGB565 || mtOut->subtype == MEDIASUBTYPE_RGB24 || mtOut->subtype == MEDIASUBTYPE_RGB32 || mtOut->subtype == MEDIASUBTYPE_ARGB32) ? NOERROR : E_FAIL; } HRESULT CDirectVobSubFilter::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) { BITMAPINFOHEADER bihIn; ExtractBIH(mtIn, &bihIn); int w = bihIn.biWidth, h = abs(bihIn.biHeight); AdjustFrameSize(w, h); BITMAPINFOHEADER bihOut; ExtractBIH(mtOut, &bihOut); if(m_pInput && m_pInput->IsConnected()) { IPin* pIn = m_pInput->GetConnected(); PIN_INFO pi; if(pIn && SUCCEEDED(pIn->QueryPinInfo(&pi))) { pi.pFilter->Release(); CLSID clsid; CComQIPtr (pi.pFilter)->GetClassID(&clsid); if(clsid == CLSID_AVIDec) { if(bihIn.biBitCount != bihOut.biBitCount) return VFW_E_TYPE_NOT_ACCEPTED; if(bihIn.biCompression != bihOut.biCompression && (bihIn.biCompression > 3 || bihOut.biCompression > 3)) return VFW_E_TYPE_NOT_ACCEPTED; } } } return (CheckInputType(mtIn) == NOERROR && CheckOutputType(mtOut) == NOERROR) ? NOERROR : VFW_E_TYPE_NOT_ACCEPTED; } HRESULT CDirectVobSubFilter::DecideBufferSize(IMemAllocator* pAlloc, ALLOCATOR_PROPERTIES* pProperties) { if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED; ASSERT(pAlloc); ASSERT(pProperties); HRESULT hr = NOERROR; pProperties->cBuffers = 1; pProperties->cbBuffer = m_bihOut.biSizeImage; ASSERT(pProperties->cbBuffer); ALLOCATOR_PROPERTIES Actual; hr = pAlloc->SetProperties(pProperties, &Actual); if(FAILED(hr)) return hr; ASSERT(Actual.cBuffers == 1); // TODO: check if the renderer has the allocator, otherwise it won't be able to dynamically change the format return(pProperties->cBuffers > Actual.cBuffers || pProperties->cbBuffer > Actual.cbBuffer ? E_FAIL : NOERROR); } #include "OutMediaTypes.h" HRESULT CDirectVobSubFilter::GetMediaType(int iPosition, CMediaType* pMediaType) { if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED; bool fVIH2 = !!(iPosition&1); iPosition >>= 1; if(m_pInput->CurrentMediaType().formattype == FORMAT_VideoInfo2) fVIH2 = !fVIH2; if(iPosition < 0) return E_INVALIDARG; if(iPosition >= VIHSIZE) return VFW_S_NO_MORE_ITEMS; AFX_MANAGE_STATE(AfxGetStaticModuleState()); BYTE* pData = NULL; UINT nSize; if(theApp.GetProfileBinary(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_COLORFORMATS), &pData, &nSize) && pData) { iPosition = pData[iPosition]; delete [] pData; } CComPtr pEnum; if(S_OK != m_pInput->GetConnected()->EnumMediaTypes(&pEnum)) return E_FAIL; bool fFound = false; for(CMediaType* pmt = NULL; S_OK == pEnum->Next(1, (AM_MEDIA_TYPE **)&pmt, 0); DeleteMediaType(pmt), pmt = NULL) { const CMediaType mt(*pmt); if(CheckInputType(&mt) == NOERROR) { fFound = true; break; } } if(pmt) { *pMediaType = *pmt; DeleteMediaType(pmt); } if(!fFound || !pmt) return VFW_S_NO_MORE_ITEMS; return ConvertMediaTypeInputToOutput(pMediaType, iPosition, fVIH2); /* int w, h, wo, ho; DWORD dwBitRate, dwBitErrorRate; REFERENCE_TIME AvgTimePerFrame; DWORD dwInterlaceFlags = 0, dwCopyProtectFlags = 0; DWORD dwPictAspectRatioX = 0, dwPictAspectRatioY = 0; DWORD dwControlFlags = 0; if(IsEqualGUID(*pMediaType->FormatType(), FORMAT_VideoInfo)) { VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pMediaType->Format(); w = wo = dwPictAspectRatioX = vih->bmiHeader.biWidth; h = ho = dwPictAspectRatioY = vih->bmiHeader.biHeight; dwBitRate = vih->dwBitRate; dwBitErrorRate = vih->dwBitErrorRate; AvgTimePerFrame = vih->AvgTimePerFrame; } else if(IsEqualGUID(*pMediaType->FormatType(), FORMAT_VideoInfo2)) { VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pMediaType->Format(); w = wo = vih->bmiHeader.biWidth; h = ho = vih->bmiHeader.biHeight; dwBitRate = vih->dwBitRate; dwBitErrorRate = vih->dwBitErrorRate; AvgTimePerFrame = vih->AvgTimePerFrame; dwInterlaceFlags = vih->dwInterlaceFlags; dwCopyProtectFlags = vih->dwCopyProtectFlags & (~AMCOPYPROTECT_RestrictDuplication); // hehe dwPictAspectRatioX = vih->dwPictAspectRatioX; dwPictAspectRatioY = vih->dwPictAspectRatioY; dwControlFlags = vih->dwControlFlags; } else // dammit { return VFW_S_NO_MORE_ITEMS; } ASSERT(h >= 0); AdjustFrameSize(w, h); if(!fVIH2) { pMediaType->SetFormatType(&FORMAT_VideoInfo); pMediaType->SetSubtype(vihs[iPosition].subtype); VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pMediaType->ReallocFormatBuffer(vihs[iPosition].size); if(!vih) return E_FAIL; memcpy(vih, &vihs[iPosition], pMediaType->FormatLength()); vih->bmiHeader.biWidth = w; vih->bmiHeader.biHeight = h; vih->bmiHeader.biSizeImage = w*h*vih->bmiHeader.biBitCount>>3; vih->AvgTimePerFrame = AvgTimePerFrame; vih->dwBitErrorRate = dwBitErrorRate; vih->dwBitRate = dwBitRate; pMediaType->SetSampleSize(vih->bmiHeader.biSizeImage); } else { pMediaType->SetFormatType(&FORMAT_VideoInfo2); pMediaType->SetSubtype(vih2s[iPosition].subtype); VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pMediaType->ReallocFormatBuffer(vih2s[iPosition].size); if(!vih) return E_FAIL; memcpy(vih, &vih2s[iPosition], pMediaType->FormatLength()); vih->bmiHeader.biWidth = w; vih->bmiHeader.biHeight = h; vih->bmiHeader.biSizeImage = w*h*vih->bmiHeader.biBitCount>>3; vih->AvgTimePerFrame = AvgTimePerFrame; vih->dwBitErrorRate = dwBitErrorRate; vih->dwBitRate = dwBitRate; vih->dwInterlaceFlags = dwInterlaceFlags; vih->dwCopyProtectFlags = dwCopyProtectFlags; vih->dwPictAspectRatioX = dwPictAspectRatioX*w/wo; vih->dwPictAspectRatioY = dwPictAspectRatioY*h/ho; vih->dwControlFlags = dwControlFlags; pMediaType->SetSampleSize(vih->bmiHeader.biSizeImage); } return NOERROR; */ } bool CDirectVobSubFilter::AdjustFrameSize(int& w, int& h) { int horizontal, vertical, resx2, resx2minw, resx2minh; get_ExtendPicture(&horizontal, &vertical, &resx2, &resx2minw, &resx2minh); bool fRet; if(fRet = (resx2 == 1) || (resx2 == 2 && w*h <= resx2minw*resx2minh)) { w <<= 1; h <<= 1; } int h2; switch(vertical&0x7f) { case 1: h2 = w * 9 / 16; if(h < h2 || !!(vertical&0x80)) h = (h2 + 3) & ~3; break; case 2: h2 = w * 3 / 4; if(h < h2 || !!(vertical&0x80)) h = (h2 + 3) & ~3; break; case 3: h2 = 480; if(h < h2 || !!(vertical&0x80)) h = (h2 + 3) & ~3; break; case 4: h2 = 576; if(h < h2 || !!(vertical&0x80)) h = (h2 + 3) & ~3; break; } if(horizontal == 1) { w = (w + 31) & ~31; // h = (h + 3) & ~3; } return(fRet); } HRESULT CDirectVobSubFilter::ConvertMediaTypeInputToOutput(CMediaType* pmt, int iVIHTemplate, bool fVIH2) { if(iVIHTemplate >= VIHSIZE) return VFW_S_NO_MORE_ITEMS; if(iVIHTemplate < 0) { if(IsEqualGUID(*pmt->FormatType(), FORMAT_VideoInfo)) { VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->Format(); for(int i = 0; i < VIHSIZE; i++) { if(*vihs[i].subtype == pmt->subtype && vihs[i].vih.bmiHeader.biCompression == vih->bmiHeader.biCompression && vihs[i].vih.bmiHeader.biBitCount == vih->bmiHeader.biBitCount) { iVIHTemplate = i; fVIH2 = false; break; } } } else if(IsEqualGUID(*pmt->FormatType(), FORMAT_VideoInfo2)) { VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->Format(); for(int i = 0; i < VIHSIZE; i++) { if(*vih2s[i].subtype == pmt->subtype && vih2s[i].vih.bmiHeader.biCompression == vih->bmiHeader.biCompression && vih2s[i].vih.bmiHeader.biBitCount == vih->bmiHeader.biBitCount) { iVIHTemplate = i; fVIH2 = true; break; } } } if(iVIHTemplate < 0) { return VFW_S_NO_MORE_ITEMS; } } int w, h, wo, ho; DWORD dwBitRate, dwBitErrorRate; REFERENCE_TIME AvgTimePerFrame; DWORD dwInterlaceFlags = 0, dwCopyProtectFlags = 0; DWORD dwPictAspectRatioX = 0, dwPictAspectRatioY = 0; DWORD dwControlFlags = 0; if(IsEqualGUID(*pmt->FormatType(), FORMAT_VideoInfo)) { VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->Format(); w = wo = dwPictAspectRatioX = vih->bmiHeader.biWidth; h = ho = dwPictAspectRatioY = vih->bmiHeader.biHeight; dwBitRate = vih->dwBitRate; dwBitErrorRate = vih->dwBitErrorRate; AvgTimePerFrame = vih->AvgTimePerFrame; } else if(IsEqualGUID(*pmt->FormatType(), FORMAT_VideoInfo2)) { VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->Format(); w = wo = vih->bmiHeader.biWidth; h = ho = vih->bmiHeader.biHeight; dwBitRate = vih->dwBitRate; dwBitErrorRate = vih->dwBitErrorRate; AvgTimePerFrame = vih->AvgTimePerFrame; dwInterlaceFlags = vih->dwInterlaceFlags; dwCopyProtectFlags = vih->dwCopyProtectFlags & (~AMCOPYPROTECT_RestrictDuplication); // hehe dwPictAspectRatioX = vih->dwPictAspectRatioX; dwPictAspectRatioY = vih->dwPictAspectRatioY; dwControlFlags = vih->dwControlFlags; } else { return VFW_S_NO_MORE_ITEMS; } ASSERT(h >= 0); AdjustFrameSize(w, h); if(!fVIH2) { pmt->SetFormatType(&FORMAT_VideoInfo); pmt->SetSubtype(vihs[iVIHTemplate].subtype); VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->ReallocFormatBuffer(vihs[iVIHTemplate].size); if(!vih) return E_FAIL; memcpy(vih, &vihs[iVIHTemplate], pmt->FormatLength()); vih->bmiHeader.biWidth = w; vih->bmiHeader.biHeight = h; vih->bmiHeader.biSizeImage = w*h*vih->bmiHeader.biBitCount>>3; vih->AvgTimePerFrame = AvgTimePerFrame; vih->dwBitErrorRate = dwBitErrorRate; vih->dwBitRate = dwBitRate; pmt->SetSampleSize(vih->bmiHeader.biSizeImage); } else { pmt->SetFormatType(&FORMAT_VideoInfo2); pmt->SetSubtype(vih2s[iVIHTemplate].subtype); VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->ReallocFormatBuffer(vih2s[iVIHTemplate].size); if(!vih) return E_FAIL; memcpy(vih, &vih2s[iVIHTemplate], pmt->FormatLength()); vih->bmiHeader.biWidth = w; vih->bmiHeader.biHeight = h; vih->bmiHeader.biSizeImage = w*h*vih->bmiHeader.biBitCount>>3; vih->AvgTimePerFrame = AvgTimePerFrame; vih->dwBitErrorRate = dwBitErrorRate; vih->dwBitRate = dwBitRate; vih->dwInterlaceFlags = dwInterlaceFlags; vih->dwCopyProtectFlags = dwCopyProtectFlags; vih->dwPictAspectRatioX = dwPictAspectRatioX*w/wo; vih->dwPictAspectRatioY = dwPictAspectRatioY*h/ho; vih->dwControlFlags = dwControlFlags; pmt->SetSampleSize(vih->bmiHeader.biSizeImage); } return NOERROR; } HRESULT CDirectVobSubFilter::SetMediaType(PIN_DIRECTION dir, const CMediaType* pmt) { if(dir == PINDIR_INPUT) { CAutoLock cAutoLock(&m_csReceive); if(!m_pOutput->IsConnected()) m_Allocator.NotifyMediaType(&m_pInput->CurrentMediaType()); ExtractBIH(pmt, &m_bihIn); REFERENCE_TIME atpf = (pmt->formattype == FORMAT_VideoInfo) ? ((VIDEOINFOHEADER*)pmt->Format())->AvgTimePerFrame : (pmt->formattype == FORMAT_VideoInfo2) ? ((VIDEOINFOHEADER2*)pmt->Format())->AvgTimePerFrame : 0; m_fps = atpf ? 10000000.0 / atpf : 25; CallWorker(CMD_RESET); } else if(dir == PINDIR_OUTPUT) { if(m_fUsingOwnAllocator && m_pInput->IsConnected()) { CComPtr pIn = m_pInput->GetConnected(); CComPtr pEnum; if(SUCCEEDED(pIn->EnumMediaTypes(&pEnum))) { bool fOK = false; for(AM_MEDIA_TYPE* pmt2; !fOK && S_OK == pEnum->Next(1, &pmt2, NULL); DeleteMediaType(pmt2)) { if(pmt->majortype == pmt2->majortype && pmt->subtype == pmt2->subtype && SUCCEEDED(pIn->QueryAccept(pmt2))) { BITMAPINFOHEADER bih, bih2; ExtractBIH(pmt, &bih); ExtractBIH(pmt2, &bih2); int w = bih2.biWidth, h = abs(bih2.biHeight); AdjustFrameSize(w, h); if(h != abs(bih.biHeight)) continue; const CMediaType mt(*pmt2); fOK = SUCCEEDED(m_pInput->SetMediaType(&mt)); if(m_pOutput->IsConnected()) m_Allocator.NotifyMediaType(&m_pInput->CurrentMediaType()); /* // This would work fine, but confuses graphedit in the drawing of its arrows/boxes. Also, we can do a dynamic format change later. if(IsStopped()) { CComQIPtr pGraph2 = m_pGraph; if(pGraph2) pGraph2->ReconnectEx(pIn, pmt); } */ } } } } ExtractBIH(pmt, &m_bihOut); } return CTransformFilter::SetMediaType(dir, pmt); } STDMETHODIMP CDirectVobSubFilter::Count(DWORD* pcStreams) { // CallWorker(CMD_BREAK); // CAutoLock cAutolock(&m_fileLock); *pcStreams = 0; if(m_file) (*pcStreams) += m_file->GetLangCountL(); (*pcStreams) += m_textlangs.GetSize(); if(*pcStreams) (*pcStreams) += 4; // enable ... disable | original flipped return S_OK; } #define MAXPREFLANGS 5 int CDirectVobSubFilter::FindPreferedLanguage(bool fHideToo) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); int len; Count((DWORD*)&len); len -= 4; if(len <= 0) return(0); for(int i = 0, j = 0; i < MAXPREFLANGS; i++) { CString tmp; tmp.Format(IDS_RL_LANG, i); CString lang = theApp.GetProfileString(ResStr(IDS_R_PREFLANGS), tmp); if(!lang.IsEmpty()) { if(fHideToo && lang == CString(ResStr(IDS_M_HIDESUBTITLES))) return(len+1); for(int ret = 1; ret <= len; ret++) { CString l; WCHAR* pNameW = NULL; Info(ret, 0, 0, 0, 0, &pNameW, 0, 0); l = pNameW; CoTaskMemFree(pNameW); if(!l.CompareNoCase(lang)) return(ret); } } } return(1); } void CDirectVobSubFilter::UpdatePreferedLanguages(CString l) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CString langs[MAXPREFLANGS+1]; for(int i = 0, j = 0, k = -1; i < MAXPREFLANGS; i++) { CString tmp; tmp.Format(IDS_RL_LANG, i); langs[j] = theApp.GetProfileString(ResStr(IDS_R_PREFLANGS), tmp); if(!langs[j].IsEmpty()) { if(!langs[j].CompareNoCase(l)) k = j; j++; } } if(k == -1) { langs[k = j] = l; j++; } // move the selected to the top of the list while(k > 0) { CString tmp = langs[k]; langs[k] = langs[k-1]; langs[k-1] = tmp; k--; } // move "Hide subtitles" to the last position if it wasn't our selection CString hidesubs; hidesubs.LoadString(IDS_M_HIDESUBTITLES); for(k = 1; k < j; k++) { if(!langs[k].CompareNoCase(hidesubs)) break; } while(k < j-1) { CString tmp = langs[k]; langs[k] = langs[k+1]; langs[k+1] = tmp; k++; } for(i = 0; i < j; i++) { CString tmp; tmp.Format(IDS_RL_LANG, i); theApp.WriteProfileString(ResStr(IDS_R_PREFLANGS), tmp, langs[i]); } } STDMETHODIMP CDirectVobSubFilter::Enable(long lIndex, DWORD dwFlags) { if(!(dwFlags & AMSTREAMSELECTENABLE_ENABLE)) return E_NOTIMPL; if(lIndex < 0) { return E_INVALIDARG; } else if(lIndex == 0) { if(!m_fLoading) // we need this because when loading something stupid media player pushes the first stream it founds, which is "enable" in our case { m_fHideSubtitles = false; int i = FindPreferedLanguage(false); if(i > 0) Enable(i, AMSTREAMSELECTENABLE_ENABLE); } return S_OK; } do { CallWorker(CMD_RESET); // this can take for a while if we are in the middle of rendering a subtitle... CAutoLock cAutolock(&m_fileLock); int orgidx = lIndex; lIndex--; if(m_file && lIndex < m_file->GetLangCountL()) { m_file->SetLangIdxL(lIndex); m_iSelectedLanguage = orgidx-1; m_fHideSubtitles = false; m_mode = VOBSUB; break; } if(m_file) lIndex -= m_file->GetLangCountL(); if(lIndex < m_textlangs.GetSize()) { m_textidx = lIndex; m_iSelectedLanguage = orgidx-1; m_fHideSubtitles = false; m_mode = TEXT; break; } lIndex -= m_textlangs.GetSize(); if(lIndex == 0) { m_fHideSubtitles = true; } else if(lIndex == 1) { if(!m_fLoading) m_fFlipPicture = false; } else if(lIndex == 2) { m_fFlipPicture = true; } } while(false); if(!m_fHideSubtitles) { int len; Count((DWORD*)&len); if(m_iSelectedLanguage >= 0 && m_iSelectedLanguage < len - 4) { WCHAR* pNameW = NULL; Info(m_iSelectedLanguage+1, 0, 0, 0, 0, &pNameW, 0, 0); CString l = pNameW; CoTaskMemFree(pNameW); UpdatePreferedLanguages(l); } } return S_OK; } STDMETHODIMP CDirectVobSubFilter::Info(long lIndex, AM_MEDIA_TYPE** ppmt, DWORD* pdwFlags, LCID* plcid, DWORD* pdwGroup, WCHAR** ppszName, IUnknown** ppObject, IUnknown** ppUnk) { // CallWorker(CMD_BREAK); // CAutoLock cAutolock(&m_fileLock); AFX_MANAGE_STATE(AfxGetStaticModuleState()); if(lIndex < 0) return S_FALSE; if(ppmt) *ppmt = CreateMediaType(&m_pInput->CurrentMediaType()); if(pdwFlags) *pdwFlags = 0; if(plcid) *plcid = 0; if(pdwGroup) *pdwGroup = 0x648E51; { CString str; if(lIndex == 0) { str.LoadString(IDS_M_SHOWSUBTITLES); if(!m_fHideSubtitles && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_ENABLED; } else { bool fFound = false; int orgidx = lIndex; lIndex--; if(!fFound && m_file) { if(lIndex < m_file->GetLangCountL()) { m_file->GetLangInfoL(lIndex, str); m_file->GetLangAltL(lIndex, str); fFound = true; } else lIndex -= m_file->GetLangCountL(); } if(!fFound) { if(lIndex < m_textlangs.GetSize()) { str = m_textlangs[lIndex].m_name; fFound = true; } else lIndex -= m_textlangs.GetSize(); } if(fFound) { if(orgidx == m_iSelectedLanguage+1 && !m_fHideSubtitles && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_ENABLED; } else if(lIndex == 0) { str.LoadString(IDS_M_HIDESUBTITLES); if(m_fHideSubtitles && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_ENABLED; } else if(lIndex == 1) { str.LoadString(IDS_M_ORIGINALPICTURE); if(!m_fFlipPicture && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_EXCLUSIVE; if(pdwGroup) (*pdwGroup)++; } else if(lIndex == 2) { str.LoadString(IDS_M_FLIPPEDPICTURE); if(m_fFlipPicture && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_EXCLUSIVE; if(pdwGroup) (*pdwGroup)++; } } if(ppszName) { *ppszName = (WCHAR*)CoTaskMemAlloc((str.GetLength()+1)*sizeof(WCHAR)); if(*ppszName == NULL) return S_FALSE; #ifdef UNICODE wcscpy(*ppszName, str); #else mbstowcs(*ppszName, str, MAX_PATH); #endif } } if(ppObject) *ppObject = NULL; if(ppUnk) *ppUnk = NULL; return S_OK; } STDMETHODIMP CDirectVobSubFilter::GetClassID(CLSID* pClsid) { if(pClsid == NULL) return E_POINTER; *pClsid = CLSID_DirectVobSubFilter; return NOERROR; } STDMETHODIMP CDirectVobSubFilter::GetPages(CAUUID* pPages) { pPages->cElems = 7; if(m_fVMRFilterActive) pPages->cElems++; pPages->pElems = (GUID *)CoTaskMemAlloc(sizeof(GUID)*pPages->cElems); if(pPages->pElems == NULL) return E_OUTOFMEMORY; int i = 0; pPages->pElems[i] = CLSID_DVSMainPPage; i++; pPages->pElems[i] = CLSID_DVSGeneralPPage; i++; pPages->pElems[i] = CLSID_DVSMiscPPage; i++; pPages->pElems[i] = CLSID_DVSTimingPPage; i++; if(m_fVMRFilterActive) {pPages->pElems[i] = CLSID_DVSZoomPPage; i++;} pPages->pElems[i] = CLSID_DVSColorPPage; i++; pPages->pElems[i] = CLSID_DVSPathsPPage; i++; pPages->pElems[i] = CLSID_DVSAboutPPage; i++; return NOERROR; } // IDirectVobSub STDMETHODIMP CDirectVobSubFilter::put_FileName(WCHAR* fn) { HRESULT hr = CDirectVobSub::put_FileName(fn); if(hr == S_OK && !Open()) { m_FileName.Empty(); hr = E_FAIL; } return hr; } STDMETHODIMP CDirectVobSubFilter::get_LanguageCount(int* nLangs) { HRESULT hr = CDirectVobSub::get_LanguageCount(nLangs); if(hr == NOERROR && nLangs) { *nLangs = (m_file?m_file->GetLangCountL():0) + m_textlangs.GetSize(); } return hr; } STDMETHODIMP CDirectVobSubFilter::get_LanguageName(int iLanguage, WCHAR** ppName) { HRESULT hr = CDirectVobSub::get_LanguageName(iLanguage, ppName); if(!ppName) return E_POINTER; if(hr == NOERROR) { if(iLanguage >= 0 && m_file) { if(iLanguage < m_file->GetLangCountL()) { CString str; m_file->GetLangInfoL(iLanguage, str); m_file->GetLangAltL(iLanguage, str); *ppName = (WCHAR*)CoTaskMemAlloc(sizeof(WCHAR)*(str.GetLength()+1)); if(!(*ppName)) E_OUTOFMEMORY; #ifdef UNICODE wcscpy(*ppName, str); #else mbstowcs(*ppName, str, str.GetLength()+1); #endif } iLanguage -= m_file->GetLangCountL(); } if(iLanguage >= 0) { if(iLanguage < m_textlangs.GetSize()) { *ppName = (WCHAR*)CoTaskMemAlloc(sizeof(WCHAR)*(m_textlangs[iLanguage].m_name.GetLength()+1)); if(!(*ppName)) E_OUTOFMEMORY; #ifdef UNICODE wcscpy(*ppName, m_textlangs[iLanguage].m_name); #else mbstowcs(*ppName, m_textlangs[iLanguage].m_name, m_textlangs[iLanguage].m_name.GetLength()+1); #endif } iLanguage -= m_textlangs.GetSize(); } if(iLanguage >= 0) { hr = E_FAIL; } } return hr; } STDMETHODIMP CDirectVobSubFilter::put_SelectedLanguage(int iSelected) { HRESULT hr = CDirectVobSub::put_SelectedLanguage(iSelected); if(hr == NOERROR) { bool fTmp = m_fHideSubtitles; Enable(m_iSelectedLanguage+1, AMSTREAMSELECTENABLE_ENABLE); m_fHideSubtitles = fTmp; } return hr; } STDMETHODIMP CDirectVobSubFilter::put_PreBuffering(bool fDoPreBuffering) { HRESULT hr = CDirectVobSub::put_PreBuffering(fDoPreBuffering); if(hr == NOERROR) { CallWorker(CMD_RESET); } return hr; } STDMETHODIMP CDirectVobSubFilter::put_Placement(bool fOverridePlacement, int xperc, int yperc) { HRESULT hr = CDirectVobSub::put_Placement(fOverridePlacement, xperc, yperc); if(hr == NOERROR) { InitRTSStreams(); CallWorker(CMD_RESET); } return hr; } STDMETHODIMP CDirectVobSubFilter::put_VobSubSettings(bool fBuffer, bool fOnlyShowForcedSubs, bool fReserved) { HRESULT hr = CDirectVobSub::put_VobSubSettings(fBuffer, fOnlyShowForcedSubs, fReserved); if(hr == NOERROR && m_file) { m_file->SetOnlyShowForcedSubs(m_fOnlyShowForcedVobSubs); CallWorker(CMD_RESET); } return hr; } STDMETHODIMP CDirectVobSubFilter::put_TextSettings(void* lf, int lflen, COLORREF color, bool fShadow, bool fOutline, bool fAdvancedRenderer) { HRESULT hr = CDirectVobSub::put_TextSettings(lf, lflen, color, fShadow, fOutline, fAdvancedRenderer); if(hr == NOERROR) { InitRTSStreams(); CallWorker(CMD_SETFONT); } return hr; } STDMETHODIMP CDirectVobSubFilter::put_SubtitleTiming(int delay, int speedmul, int speeddiv) { HRESULT hr = CDirectVobSub::put_SubtitleTiming(delay, speedmul, speeddiv); if(hr == NOERROR) { CallWorker(CMD_RESET); } return hr; } STDMETHODIMP CDirectVobSubFilter::get_MediaFPS(bool* fEnabled, double* fps) { HRESULT hr = CDirectVobSub::get_MediaFPS(fEnabled, fps); CComQIPtr pMS = m_pGraph; double rate; if(pMS && SUCCEEDED(pMS->GetRate(&rate))) { m_MediaFPS = rate * m_fps; if(fps) *fps = m_MediaFPS; } return hr; } STDMETHODIMP CDirectVobSubFilter::put_MediaFPS(bool fEnabled, double fps) { HRESULT hr = CDirectVobSub::put_MediaFPS(fEnabled, fps); CComQIPtr pMS = m_pGraph; if(pMS) { if(hr == NOERROR) { hr = pMS->SetRate(m_fMediaFPSEnabled ? m_MediaFPS / m_fps : 1.0); TRACE(_T("SetRate\n")); } double dRate; if(SUCCEEDED(pMS->GetRate(&dRate))) m_MediaFPS = dRate * m_fps; CString str; str.Format(_T("%f, %f\n"), dRate, m_MediaFPS); TRACE(str); } return hr; } STDMETHODIMP CDirectVobSubFilter::get_ZoomRect(NORMALIZEDRECT* rect) { if(!m_fVMRFilterActive || !m_pOutput || !m_pOutput->IsConnected() || !rect) return(E_FAIL); HRESULT hr; CComQIPtr pVMRMC = FindVMRFilterFromPin(m_pOutput->GetConnected()); if(pVMRMC && FAILED(hr = pVMRMC->GetOutputRect(0, &m_ZoomRect))) return hr; return CDirectVobSub::get_ZoomRect(rect); } STDMETHODIMP CDirectVobSubFilter::put_ZoomRect(NORMALIZEDRECT* rect) { if(!m_fVMRFilterActive || !m_pOutput || !m_pOutput->IsConnected()) return(E_FAIL); HRESULT hr; CComQIPtr pVMRMC = FindVMRFilterFromPin(m_pOutput->GetConnected()); if(pVMRMC && FAILED(hr = pVMRMC->SetOutputRect(0, rect))) return hr; return CDirectVobSub::put_ZoomRect(rect); } // IDirectVobSubFilterColor STDMETHODIMP CDirectVobSubFilter::get_ColorFormat(int* iPosition) { if(!m_pOutput || !m_pOutput->IsConnected() || !iPosition) return E_FAIL; BITMAPINFOHEADER bih, bih2; ExtractBIH(&m_pOutput->CurrentMediaType(), &bih); const GUID& subtype = m_pOutput->CurrentMediaType().subtype; *iPosition = 0; CMediaType mt; while(SUCCEEDED(GetMediaType((*iPosition)<<1, &mt))) { ExtractBIH(&mt, &bih2); if(mt.subtype == subtype && bih2.biBitCount == bih.biBitCount && bih2.biCompression == bih.biCompression) { return S_OK; } (*iPosition)++; } return E_FAIL; } HRESULT CDirectVobSubFilter::ChangeMediaType(int iPosition) { if(!m_pOutput || !m_pOutput->IsConnected() || m_State == State_Paused) return E_FAIL; CAutoLock cAutoLock(&m_csReceive); CComPtr pPin = m_pOutput->GetConnected(); if(!pPin) return E_FAIL; CComQIPtr pPinConnection = pPin; CComQIPtr pMemImputPin = pPin; if(!pPinConnection || !pMemImputPin) return E_FAIL; CComPtr pAllocator; if(FAILED(pMemImputPin->GetAllocator(&pAllocator))) return E_FAIL; CMediaType orgmt; orgmt = m_pOutput->CurrentMediaType(); HRESULT hr; CMediaType mt; GetMediaType(iPosition<<1, &mt); if(SUCCEEDED(hr = pPinConnection->DynamicQueryAccept(&mt))) { if(SUCCEEDED(hr = pPin->ReceiveConnection(m_pOutput, &mt))) { // this shouldn't be needed, but the old renderer won't attach // the new media type to the next sample, despite the fact that // it has just accepted it... m_pOutput->SetMediaType(&mt); return(NOERROR); } } return(E_FAIL); } STDMETHODIMP CDirectVobSubFilter::put_ColorFormat(int iPosition) { if(!m_pOutput || !m_pOutput->IsConnected() /*|| !m_fVMRFilter*/) return E_FAIL; return ChangeMediaType(iPosition); } STDMETHODIMP CDirectVobSubFilter::HasConfigDialog(int iSelected) { int nLangs; if(FAILED(get_LanguageCount(&nLangs))) return E_FAIL; return(nLangs >= 0 && iSelected < nLangs ? S_OK : E_FAIL); } STDMETHODIMP CDirectVobSubFilter::ShowConfigDialog(int iSelected, HWND hWndParent) { if(iSelected < 0 && FAILED(get_SelectedLanguage(&iSelected))) return E_FAIL; if(m_file) { if(iSelected < m_file->GetLangCountL()) { bool fRet = m_file->ConfigDlg(false, hWndParent); if(fRet) { m_file->SetLangIdxL(iSelected); CallWorker(CMD_RESET); } return(fRet ? S_OK : S_FALSE); } iSelected -= m_file->GetLangCountL(); } if(iSelected < m_textlangs.GetSize()) return(m_textlangs[iSelected].ConfigDlg(0, hWndParent) ? CallWorker(CMD_RESET), S_OK : S_FALSE); iSelected -= m_textlangs.GetSize(); return(E_FAIL); } //////////////////////////////////////////////////////////////////////// STDMETHODIMP CDirectVobSubFilter::QueryFilterInfo(FILTER_INFO* pInfo) { CheckPointer(pInfo, E_POINTER); ValidateReadWritePtr(pInfo, sizeof(FILTER_INFO)); HRESULT hr = E_FAIL; if(get_Forced()) { wcscpy(pInfo->achName, L"DirectVobSub (forced auto-loading version)"); if(pInfo->pGraph = m_pGraph) m_pGraph->AddRef(); hr = NOERROR; } else { hr = CTransformFilter::QueryFilterInfo(pInfo); } return(hr); } STDMETHODIMP CDirectVobSubFilter::SetSyncSource(IReferenceClock* pClock) { if(!m_fAudioReconnected && m_pGraph) { CComPtr pBF; if(pBF = FindAudioRenderer(m_pGraph)) { CComPtr pPin; if(pPin = FindFirstPin(pBF)) { HRESULT hr = m_pGraph->Reconnect(pPin); m_fAudioReconnected = SUCCEEDED(hr); } } } return CTransformFilter::SetSyncSource(pClock); } void CDirectVobSubFilter::HookTextStreams() // nasty... { if(m_fTextStreamWorkaroundDone) return; { int i = 0; WCHAR buff[200] = L"Internal Script Command Renderer"; for(CComPtr pFilter; S_OK == m_pGraph->FindFilterByName(buff, &pFilter); pFilter = NULL) { HRESULT hr = m_pGraph->RemoveFilter(pFilter); i++; swprintf(buff, L"Internal Script Command Renderer %04d", i); } } CComPtr pFilter; if(FAILED(m_pGraph->FindFilterByName(L"AVI Splitter", &pFilter))) return; CComPtr pEnum; if(FAILED(pFilter->EnumPins(&pEnum))) return; for(CComPtr pPin; S_OK == pEnum->Next(1, &pPin, 0); pPin = NULL) { PIN_DIRECTION pd; if(FAILED(pPin->QueryDirection(&pd)) || pd != PINDIR_OUTPUT) continue; CComPtr pPin2; if(SUCCEEDED(pPin->ConnectedTo(&pPin2))) continue; CComPtr pEnum2; if(FAILED(pPin->EnumMediaTypes(&pEnum2))) continue; bool fOK = false; for(AM_MEDIA_TYPE* pmt; !fOK && S_OK == pEnum2->Next(1, &pmt, NULL); DeleteMediaType(pmt)) { if(pmt->majortype == MEDIATYPE_Text) { m_pGraph->ConnectDirect(pPin, GetPin(2 + m_pTextInput.GetSize()-1), pmt); fOK = true; } } } m_fTextStreamWorkaroundDone = true; } /////////////////////////////////////////////////////////////////////////// CDirectVobSubFilter2::CDirectVobSubFilter2(TCHAR* tszName, LPUNKNOWN punk, HRESULT* phr, const GUID& guid) : CDirectVobSubFilter(tszName, punk, phr, guid) { } CUnknown* CDirectVobSubFilter2::CreateInstance(LPUNKNOWN punk, HRESULT* phr) { CDirectVobSubFilter2* pNewObject = new CDirectVobSubFilter2(NAME("DirectVobSub (auto-loading version)"), punk, phr, CLSID_DirectVobSubFilter2); if(pNewObject == NULL) *phr = E_OUTOFMEMORY; return pNewObject; } STDMETHODIMP CDirectVobSubFilter2::GetClassID(CLSID* pClsid) { if(pClsid == NULL) return E_POINTER; *pClsid = CLSID_DirectVobSubFilter2; return NOERROR; } HRESULT CDirectVobSubFilter2::CheckConnect(PIN_DIRECTION dir, IPin* pPin) { PIN_INFO pi; if(FAILED(pPin->QueryPinInfo(&pi))) return E_FAIL; CComPtr pFilter; pFilter.Attach(pi.pFilter); if(CComQIPtr (pFilter)) return(E_FAIL); if(dir == PINDIR_INPUT) { FILTER_INFO fi; if(SUCCEEDED(pFilter->QueryFilterInfo(&fi))) { if(fi.pGraph) fi.pGraph->Release(); if(!wcsnicmp(fi.achName, L"Overlay Mixer", 13)) return(E_FAIL); } } else { } return CDirectVobSubFilter::CheckConnect(dir, pPin); } HRESULT CDirectVobSubFilter2::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName) { if(pGraph) { if(FindMyself(pGraph) > 1) return E_FAIL; // don't look... we will do some serious graph hacking again... // // we will add dvs2 to the filter graph cache // - if the main app has already added some kind of renderer or overlay mixer (anything which accepts video on its input) // and // - if we have a reason to auto-load (we don't want to make any trouble when there is no need :) // // This whole workaround is needed because the video stream will always be connect // to the pre-added filters first, no matter how high merit we have. if(!get_Forced()) { CComPtr pEnum; if(SUCCEEDED(pGraph->EnumFilters(&pEnum))) { bool fOK = false; for(CComPtr pFilter; !fOK && (S_OK == pEnum->Next(1, &pFilter, NULL)); pFilter = NULL) { if(CComQIPtr (pFilter)) continue; CComPtr pEnum2; if(FAILED(pFilter->EnumPins(&pEnum2))) continue; CComPtr pPin, pInPin, pOutPin; for(; S_OK == pEnum2->Next(1, &pPin, NULL); pPin = NULL) { PIN_DIRECTION dir; if(SUCCEEDED(pPin->QueryDirection(&dir))) { if(dir == PINDIR_INPUT && !pInPin) pInPin = pPin; else if(dir == PINDIR_OUTPUT && !pOutPin) pOutPin = pPin; } } if(!pInPin && !pOutPin) continue; FILTER_INFO fi; if(FAILED(pFilter->QueryFilterInfo(&fi))) continue; if(fi.pGraph) fi.pGraph->Release(); if(pOutPin && !wcsstr(fi.achName, L"Overlay Mixer")) continue; pPin = NULL; if(pInPin && SUCCEEDED(pInPin->ConnectedTo(&pPin))) continue; pPin = NULL; if(pOutPin && SUCCEEDED(pOutPin->ConnectedTo(&pPin))) continue; bool fVideoInputPin = false; do { BITMAPINFOHEADER bih; memset(&bih, 0, sizeof(bih)); bih.biSize = sizeof(bih); bih.biWidth = 384; bih.biHeight = 288; bih.biBitCount = 16; bih.biCompression = mmioFOURCC('Y','U','Y','2'); bih.biSizeImage = 384*288*2; bih.biPlanes = 1; CMediaType cmt; cmt.majortype = MEDIATYPE_Video; cmt.subtype = MEDIASUBTYPE_YUY2; cmt.formattype = FORMAT_VideoInfo; cmt.pUnk = NULL; cmt.bFixedSizeSamples = TRUE; cmt.bTemporalCompression = TRUE; cmt.lSampleSize = 384*288*2; VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)cmt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER)); memset(vih, 0, sizeof(VIDEOINFOHEADER)); memcpy(&vih->bmiHeader, &bih, sizeof(bih)); vih->AvgTimePerFrame = 400000; if(SUCCEEDED(pInPin->QueryAccept(&cmt))) { fVideoInputPin = true; break; } VIDEOINFOHEADER2* vih2 = (VIDEOINFOHEADER2*)cmt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER2)); memset(vih2, 0, sizeof(VIDEOINFOHEADER2)); memcpy(&vih2->bmiHeader, &bih, sizeof(bih)); vih2->AvgTimePerFrame = 400000; vih2->dwPictAspectRatioX = 384; vih2->dwPictAspectRatioY = 288; if(SUCCEEDED(pInPin->QueryAccept(&cmt))) { fVideoInputPin = true; break; } } while(false); if(fVideoInputPin) { CComPtr pDVS; if(ShouldWeAutoload(pGraph) && SUCCEEDED(pDVS.CoCreateInstance(CLSID_DirectVobSubFilter2))) { CComQIPtr (pDVS)->put_Forced(true); CComQIPtr (pGraph)->AddFilterToCache(pDVS); } fOK = true; } } } } } else { } return CDirectVobSubFilter::JoinFilterGraph(pGraph, pName); } HRESULT CDirectVobSubFilter2::CheckInputType(const CMediaType* mtIn) { HRESULT hr = CDirectVobSubFilter::CheckInputType(mtIn); if(FAILED(hr) || m_pInput->IsConnected()) return hr; if(!ShouldWeAutoload(m_pGraph)) return VFW_E_TYPE_NOT_ACCEPTED; GetRidOfInternalScriptRenderer(); return NOERROR; } bool CDirectVobSubFilter2::ShouldWeAutoload(IFilterGraph* pGraph) { TCHAR blacklistedapps[][32] = { _T("WM8EUTIL."), // wmp8 encoder's dummy renderer releases the outputted media sample after calling Receive on its input pin (yes, even when dvobsub isn't registered at all) _T("explorer."), // as some users reported thumbnail preview loads dvobsub, I've never experienced this yet... }; for(int i = 0; i < sizeof(blacklistedapps)/sizeof(blacklistedapps[0]); i++) { if(theApp.m_AppName.Find(blacklistedapps[i]) >= 0) return(false); } int level; bool m_fExternalLoad, m_fWebLoad, m_fEmbeddedLoad; get_LoadSettings(&level, &m_fExternalLoad, &m_fWebLoad, &m_fEmbeddedLoad); if(level < 0 || level >= 2) return(false); bool fRet = false; if(level == 1) fRet = m_fExternalLoad = m_fWebLoad = m_fEmbeddedLoad = true; fRet = (m_fEmbeddedLoad && FindTextStream(pGraph)) || fRet; WCHAR fn[MAX_PATH] = {0}; if((m_fExternalLoad || m_fWebLoad) && FindSourceFileName(pGraph, fn) && (m_fWebLoad || !(wcsstr(fn, L"http://") || wcsstr(fn, L"mms://")))) { bool fTemp = m_fHideSubtitles; fRet = SUCCEEDED(put_FileName(fn)) || fRet; if(fTemp) m_fHideSubtitles = true; } return(fRet); } void CDirectVobSubFilter2::GetRidOfInternalScriptRenderer() { int i = 0; WCHAR buff[100] = L"Internal Script Command Renderer"; for(CComPtr pFilter; S_OK == m_pGraph->FindFilterByName(buff, &pFilter); pFilter = NULL) { i++; swprintf(buff, L"Internal Script Command Renderer %04d", i); CComPtr pEnum; if(FAILED(pFilter->EnumPins(&pEnum))) continue; for(CComPtr pPin; S_OK == pEnum->Next(1, &pPin, 0); pPin = NULL) { PIN_DIRECTION pd; CComPtr pPin2; if(SUCCEEDED(pPin->QueryDirection(&pd)) && pd == PINDIR_INPUT && SUCCEEDED(pPin->ConnectedTo(&pPin2))) { m_pGraph->Disconnect(pPin2); m_pGraph->ConnectDirect(pPin2, GetPin(2 + m_pTextInput.GetSize()-1), NULL); } } m_pGraph->RemoveFilter(pFilter); } } /////////////////////////////////////////////////////////////////////////////// static void PixelAtBasic(RGBQUAD& c, int x, int y, CVobSubImage& src) { c = src.lpPixels[(y >> 16) * src.rect.Width() + (x >> 16)]; } static void PixelAtBiLinear(RGBQUAD& c, int x, int y, CVobSubImage& src) { int w = src.rect.Width(), h = src.rect.Height(); int x1 = (x >> 16), y1 = (y >> 16) * w, x2 = min(x1 + 1, w-1), y2 = min(y1 + w, (h-1)*w); RGBQUAD* ptr = src.lpPixels; RGBQUAD c11 = ptr[y1 + x1], c12 = ptr[y1 + x2], c21 = ptr[y2 + x1], c22 = ptr[y2 + x2]; __int64 u2 = x & 0xffff, v2 = y & 0xffff, u1 = 0x10000 - u2, v1 = 0x10000 - v2; int v1u1 = int(v1*u1 >> 16) * c11.rgbReserved, v1u2 = int(v1*u2 >> 16) * c12.rgbReserved, v2u1 = int(v2*u1 >> 16) * c21.rgbReserved, v2u2 = int(v2*u2 >> 16) * c22.rgbReserved; c.rgbRed = (c11.rgbRed * v1u1 + c12.rgbRed * v1u2 + c21.rgbRed * v2u1 + c22.rgbRed * v2u2) >> 24; c.rgbGreen = (c11.rgbGreen * v1u1 + c12.rgbGreen * v1u2 + c21.rgbGreen * v2u1 + c22.rgbGreen * v2u2) >> 24; c.rgbBlue = (c11.rgbBlue * v1u1 + c12.rgbBlue * v1u2 + c21.rgbBlue * v2u1 + c22.rgbBlue * v2u2) >> 24; c.rgbReserved = (v1u1 + v1u2 + v2u1 + v2u2) >> 16; } static void PixelAtBiCubic(RGBQUAD& c, int x, int y, CVobSubImage& src) { int w = src.rect.Width(), h = src.rect.Height(); int x1 = (x >> 16), y1 = (y >> 16) * w, x0 = max(x1 - 1, 0), y0 = max(y1 - w, 0), x2 = min(x1 + 1, w-1), y2 = min(y1 + w, (h-1)*w); RGBQUAD* ptr = src.lpPixels; RGBQUAD c00 = ptr[y0 + x0], c01 = ptr[y0 + x1], c02 = ptr[y0 + x2], c10 = ptr[y1 + x0], c11 = ptr[y1 + x1], c12 = ptr[y1 + x2], c20 = ptr[y2 + x0], c21 = ptr[y2 + x1], c22 = ptr[y2 + x2]; __int64 u2 = ((x & 0xffff) | 0x10000) >> 1, v2 = ((y & 0xffff) | 0x10000) >> 1, u1 = 0x10000 - u2, v1 = 0x10000 - v2; __int64 u11 = int(u1*u1 >> 16), u12 = int(u1*u2 >> 15), u22 = int(u2*u2 >> 16), v11 = int(v1*v1 >> 16), v12 = int(v1*v2 >> 15), v22 = int(v2*v2 >> 16); int v11u11 = int(u11*v11 >> 16) * c00.rgbReserved, v11u12 = int(u12*v11 >> 16) * c01.rgbReserved, v11u22 = int(u22*v11 >> 16) * c02.rgbReserved, v12u11 = int(u11*v12 >> 16) * c10.rgbReserved, v12u12 = int(u12*v12 >> 16) * c11.rgbReserved, v12u22 = int(u22*v12 >> 16) * c12.rgbReserved, v22u11 = int(u11*v22 >> 16) * c20.rgbReserved, v22u12 = int(u12*v22 >> 16) * c21.rgbReserved, v22u22 = int(u22*v22 >> 16) * c22.rgbReserved; c.rgbRed = (c00.rgbRed * v11u11 + c01.rgbRed * v11u12 + c02.rgbRed * v11u22 + c10.rgbRed * v12u11 + c11.rgbRed * v12u12 + c12.rgbRed * v12u22 + c20.rgbRed * v22u11 + c21.rgbRed * v22u12 + c22.rgbRed * v22u22) >> 24; c.rgbGreen = (c00.rgbGreen * v11u11 + c01.rgbGreen * v11u12 + c02.rgbGreen * v11u22 + c10.rgbGreen * v12u11 + c11.rgbGreen * v12u12 + c12.rgbGreen * v12u22 + c20.rgbGreen * v22u11 + c21.rgbGreen * v22u12 + c22.rgbGreen * v22u22) >> 24; c.rgbBlue = (c00.rgbBlue * v11u11 + c01.rgbBlue * v11u12 + c02.rgbBlue * v11u22 + c10.rgbBlue * v12u11 + c11.rgbBlue * v12u12 + c12.rgbBlue * v12u22 + c20.rgbBlue * v22u11 + c21.rgbBlue * v22u12 + c22.rgbBlue * v22u22) >> 24; c.rgbReserved = (v11u11 + v11u12 + v11u22 + v12u11 + v12u12 + v12u22 + v22u11 + v22u12 + v22u22) >> 16; // Oh-hoh-hoo! See those stars? They are beautiful! Ah yes, and don't forget to buy a P4, LOL ;-) } void StretchPut(BITMAP& bm, CRect dstrect, CVobSubImage& src, int fSmooth) { if(dstrect.IsRectEmpty()) return; if((dstrect & CRect(0, 0, bm.bmWidth, bm.bmHeight)).IsRectEmpty()) return; int sw = src.rect.Width(), sh = src.rect.Height(), dw = dstrect.Width(), dh = dstrect.Height(); int srcx = 0, srcy = 0, srcdx = (sw << 16) / dw >> 1, srcdy = (sh << 16) / dh >> 1; if(dstrect.left < 0) {srcx = -dstrect.left * (srcdx<<1); dstrect.left = 0;} if(dstrect.top < 0) {srcy = -dstrect.top * (srcdy<<1); dstrect.top = 0;} if(dstrect.right > bm.bmWidth) {dstrect.right = bm.bmWidth;} if(dstrect.bottom > bm.bmHeight) {dstrect.bottom = bm.bmHeight;} if((dstrect & CRect(0, 0, bm.bmWidth, bm.bmHeight)).IsRectEmpty()) return; dw = dstrect.Width(); dh = dstrect.Height(); if(fSmooth < 2) { void (* PixelAt)(RGBQUAD&, int, int, CVobSubImage&) = fSmooth == 0 ? PixelAtBiLinear : PixelAtBiCubic; for(int y = dstrect.top; y < dstrect.bottom; y++, srcy += (srcdy<<1)) { RGBQUAD* ptr = (RGBQUAD*)&((char*)bm.bmBits)[(bm.bmHeight-y-1)*bm.bmWidthBytes] + dstrect.left; RGBQUAD* endptr = ptr + dw; for(int sx = srcx; ptr < endptr; sx += (srcdx<<1), ptr++) { PixelAt(*ptr, sx, srcy, src); RGBQUAD cc[4]; PixelAt(cc[0], sx, srcy, src); PixelAt(cc[1], sx+srcdx, srcy, src); PixelAt(cc[2], sx, srcy+srcdy, src); PixelAt(cc[3], sx+srcdx, srcy+srcdy, src); ptr->rgbRed = (cc[0].rgbRed + cc[1].rgbRed + cc[2].rgbRed + cc[3].rgbRed) >> 2; ptr->rgbGreen = (cc[0].rgbGreen + cc[1].rgbGreen + cc[2].rgbGreen + cc[3].rgbGreen) >> 2; ptr->rgbBlue = (cc[0].rgbBlue + cc[1].rgbBlue + cc[2].rgbBlue + cc[3].rgbBlue) >> 2; ptr->rgbReserved = (cc[0].rgbReserved + cc[1].rgbReserved + cc[2].rgbReserved + cc[3].rgbReserved) >> 2; ptr->rgbRed = ptr->rgbRed * ptr->rgbReserved >> 8; ptr->rgbGreen = ptr->rgbGreen * ptr->rgbReserved >> 8; ptr->rgbBlue = ptr->rgbBlue * ptr->rgbReserved >> 8; ptr->rgbReserved = ~ptr->rgbReserved; } } } else { for(int y = dstrect.top; y < dstrect.bottom; y++, srcy += (srcdy<<1)) { RGBQUAD* ptr = (RGBQUAD*)&((char*)bm.bmBits)[(bm.bmHeight-y-1)*bm.bmWidthBytes] + dstrect.left; RGBQUAD* endptr = ptr + dw; for(int sx = srcx; ptr < endptr; sx += (srcdx<<1), ptr++) { PixelAtBasic(*ptr, sx, srcy, src); ptr->rgbRed = ptr->rgbRed * ptr->rgbReserved >> 8; ptr->rgbGreen = ptr->rgbGreen * ptr->rgbReserved >> 8; ptr->rgbBlue = ptr->rgbBlue * ptr->rgbReserved >> 8; ptr->rgbReserved = ~ptr->rgbReserved; } } } } //////////////////////////////////////////////////////////////////////// bool CDirectVobSubFilter::Open() { AFX_MANAGE_STATE(AfxGetStaticModuleState()); bool fRet = false; { CallWorker(CMD_RESET); CAutoLock lock(&m_AccessLock); CAutoLock cAutolock(&m_fileLock); m_mode = NONE; m_iSelectedLanguage = -1; m_fLoading = true; { if(m_file) {delete m_file; m_file = NULL;} m_textlangs.RemoveAll(); } m_frd.files.RemoveAll(); CStringArray paths; for(int k = 0; k < 10; k++) { CString path, tmp; tmp.Format(IDS_RP_PATH, k); path = theApp.GetProfileString(ResStr(IDS_R_DEFTEXTPATHES), tmp); if(!path.IsEmpty()) paths.Add(path); } SubFiles sf; vobsubGetSubFileNames(m_FileName, paths, sf); for(int i = 0, j = sf.GetSize(), ssa = 0; i < j; i++) { SubFile& osf = sf[i]; if(osf.ext == EXTIDX) { if(m_file) continue; m_file = new CVobSubFile(); if(!m_file) continue; CString title = osf.fn.Left(osf.fn.ReverseFind('.')); if(!m_file->Open(title, m_fBufferVobSub)) { delete m_file; m_file = NULL; continue; } m_file->SetOnlyShowForcedSubs(m_fOnlyShowForcedVobSubs); m_frd.files.Add(sf[i].fn.Left(sf[i].fn.GetLength()-4) + _T(".sub")); } else { CRenderedTextSubtitle ret; if(!ret.Open(sf[i].fn, DEFAULT_CHARSET)) continue; m_textlangs.Add(ret); m_frd.files.Add(sf[i].fn + _T(".style")); } m_frd.files.Add(sf[i].fn); } SetEvent(m_frd.hRefreshEvent); } for(int i = 0; i < m_pTextInput.GetSize(); i++) { if(!m_pTextInput[i] || !m_pTextInput[i]->IsConnected()) continue; AddTextStream(m_pTextInput[i]->GetName()); } InitRTSStreams(false); Enable(FindPreferedLanguage(), AMSTREAMSELECTENABLE_ENABLE); DWORD cnt; Count(&cnt); return(cnt > 4); } static int intcomp(const void* i1, const void* i2) { return(*((int*)i1) - *((int*)i2)); } bool CDirectVobSubFilter::InitRTSStreams(bool fApplyDefStyle) { if(!m_pOutput || !m_pOutput->IsConnected()) return(false); CallWorker(CMD_BREAK); { CAutoLock cAutolock(&m_fileLock); for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) { if(!m_textlangs[i].Init(CSize(m_sic.w, m_sic.h))) return(false); STSStyle s; if(!m_textlangs[i].GetDefaultStyle(s) || (!fApplyDefStyle && !m_textlangs[i].m_fUsingDefaultStyle)) continue; if(m_fOverridePlacement) { s.scrAlignment = 2; int w = m_textlangs[i].m_dstScreenSize.cx; int h = m_textlangs[i].m_dstScreenSize.cy; int mw = w - s.marginRect.left - s.marginRect.right; s.marginRect.bottom = h - MulDiv(h, m_PlacementYperc, 100); s.marginRect.left = MulDiv(w, m_PlacementXperc, 100) - mw/2; s.marginRect.right = w - (s.marginRect.left + mw); } s = m_lf; s.colors[0] = m_TextColor; s.outlineWidth = m_fOutline?2:0; s.shadowDepth = m_fShadow?2:0; m_textlangs[i].SetDefaultStyle(s); } } return(true); } ////////////////////////////////////////////////////////////////////////////////////////// void CDirectVobSubFilter::AddTextStream(CString name) { CallWorker(CMD_BREAK); { CAutoLock cAutolock(&m_fileLock); RemoveTextStream(name); CRenderedTextSubtitle rts; rts.m_name = name; rts.m_mode = TIME; rts.m_dstScreenSize = CSize(384, 288); rts.m_fSSA = true; rts.CreateDefaultStyle(DEFAULT_CHARSET, false); STSStyle s; if(rts.GetDefaultStyle(s)) { if(m_fOverridePlacement) { s.scrAlignment = 2; int w = rts.m_dstScreenSize.cx; int h = rts.m_dstScreenSize.cy; int mw = w - s.marginRect.left - s.marginRect.right; s.marginRect.bottom = h - MulDiv(h, m_PlacementYperc, 100); s.marginRect.left = MulDiv(w, m_PlacementXperc, 100) - mw/2; s.marginRect.right = w - (s.marginRect.left + mw); } s = m_lf; s.colors[0] = m_TextColor; s.outlineWidth = m_fOutline?2:0; s.shadowDepth = m_fShadow?2:0; rts.SetDefaultStyle(s); } m_textlangs.Add(rts); m_textidx = 0; } bool fTemp = m_fHideSubtitles; Enable(1, AMSTREAMSELECTENABLE_ENABLE); m_fHideSubtitles = fTemp; } void CDirectVobSubFilter::AddNewEntryToTextStream(CString name, CString str, int start, int end) { CallWorker(CMD_BREAK); { CAutoLock cAutolock(&m_fileLock); for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) { if(m_textlangs[i].m_name == name) { start = (int)(start - fmod(1.0*start, 1000.0/fabs(m_MediaFPS)) + 0.5); end = (int)(end - fmod(1.0*end, 1000.0/fabs(m_MediaFPS)) + 0.5); if(start == end) end = (int)(end + 1000.0/fabs(m_MediaFPS) + 0.5); m_textlangs[i].Add(str, start, end); if(m_textidx == i) m_sic.FreeExtra(start); break; } } } } void CDirectVobSubFilter::ChangeName(CString oldname, CString newname, bool fUnicode) { /* CallWorker(CMD_BREAK); CAutoLock cAutolock(&m_fileLock); */ for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) { if(m_textlangs[i].m_name == oldname) { m_textlangs[i].m_name = newname; if(m_textlangs[i].GetSize() > 0) m_textlangs[i].GetStyle(0)->fUnicode = fUnicode; break; } } } void CDirectVobSubFilter::ChangeName(CString oldname, CString newname) { for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) { if(m_textlangs[i].m_name == oldname) { m_textlangs[i].m_name = newname; break; } } } void CDirectVobSubFilter::RemoveEntriesFromTextStream(CString name) { /* CallWorker(CMD_RESET); { CAutoLock cAutolock(&m_fileLock); for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) { if(m_textlangs[i].m_name == name) { m_textlangs[i].Empty(); break; } } } */ } void CDirectVobSubFilter::RemoveTextStream(CString name) { CallWorker(CMD_BREAK); { CAutoLock cAutolock(&m_fileLock); for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) { if(m_textlangs[i].m_name == name) { m_textlangs.RemoveAt(i); break; } } } } void CDirectVobSubFilter::OpenRawTextSubtitle(CString name, BYTE* data, int len) { CallWorker(CMD_BREAK); { CAutoLock cAutolock(&m_fileLock); for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) { CRenderedTextSubtitle& rts = m_textlangs[i]; if(rts.m_name == name) { if(rts.GetSize() == 0) { CallWorker(CMD_RESET); rts.Open(data, len, DEFAULT_CHARSET, name); STSStyle s; if(rts.GetDefaultStyle(s)) { if(m_fOverridePlacement) { s.scrAlignment = 2; int w = rts.m_dstScreenSize.cx; int h = rts.m_dstScreenSize.cy; int mw = w - s.marginRect.left - s.marginRect.right; s.marginRect.bottom = h - MulDiv(h, m_PlacementYperc, 100); s.marginRect.left = MulDiv(w, m_PlacementXperc, 100) - mw/2; s.marginRect.right = w - (s.marginRect.left + mw); } s = m_lf; s.colors[0] = m_TextColor; s.outlineWidth = m_fOutline?2:0; s.shadowDepth = m_fShadow?2:0; rts.SetDefaultStyle(s); } } break; } } } } ////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// DWORD CDirectVobSubFilter::ThreadProc() { SetThreadPriority(m_hThread, THREAD_PRIORITY_BELOW_NORMAL); while(1) { DWORD cmd = GetRequest(); m_fThreadActive = true; // don't call us again with CMD_SETPTS when this flag is set switch(cmd) { case CMD_EXIT: DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_EXIT"))); Reply(S_OK); return 0; // wow, look at this, no breaks through 4 choices :) case CMD_INIT: DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_INIT"))); m_sic.Init(m_bihSub.biWidth, m_bihSub.biHeight); case CMD_SETFONT: DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_SETFONT"))); { LOGFONT tmp; memcpy(&tmp, &m_lf, sizeof(tmp)); HDC hdc = ::GetDC(0); tmp.lfHeight = -MulDiv(tmp.lfHeight, 72 * m_bihSub.biHeight, GetDeviceCaps(hdc, LOGPIXELSY) * 288); ReleaseDC(0, hdc); m_sic.SetFont(&tmp); } case CMD_RESET: DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_RESET"))); m_sic.RemoveAll(); case CMD_BREAK: DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_BREAK"))); Reply(S_OK); break; case CMD_SETPTS: DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_SETPTS %d"), (int)m_tPrev)); { Reply(S_OK); int PTS = CalcCurrentTime(); m_sic.MarkAsStart(PTS); if(!m_sic.CachedEnough(PTS)) { PTS = m_sic.SeekToEnd(PTS); int i, j, endPTS = PTS + LOOKAHEADSIZE; SubImage* cur = NULL; CAutoLock cAutoLock(&m_fileLock); if(m_mode == VOBSUB && m_file != NULL) { i = m_file->GetFrameIdxByTimeStamp(PTS), j = m_file->GetFrameIdxByTimeStamp(PTS + LOOKAHEADSIZE); if(j == -1) { PTS += LOOKAHEADSIZE; } else if(i == -1) { while((i = m_file->GetFrameIdxByTimeStamp(PTS)) == -1 && PTS < endPTS) { PTS += LOOKAHEADSTEPSIZE; } } for(; PTS < endPTS && i >= 0 && m_file->GetFrame(i) && (cur = m_sic.GetNextFree()) != NULL; i++) { if(CheckRequest(&cmd) || m_fBreakBuffering) break; int start = m_file->m_img.nTimeStamp; int stop = m_file->m_img.nTimeStamp + m_file->m_img.nDisplayTime; if(PTS < stop) { AddVobSub(cur, start, stop); PTS = stop; } } } else if(m_mode == TEXT && m_textidx >= 0 && m_textidx < m_textlangs.GetSize()) { CRenderedTextSubtitle& rts = m_textlangs[m_textidx]; int iSegment = -1, nSegments = 0; rts.SearchSubs(PTS, m_fps, &iSegment, &nSegments); if(iSegment < 0 && nSegments > 0) iSegment++; if(iSegment >= nSegments) PTS += LOOKAHEADSIZE; for(; PTS < endPTS && iSegment >= 0 && iSegment < nSegments && (cur = m_sic.GetNextFree()) != NULL; iSegment++) { if(CheckRequest(&cmd) || m_fBreakBuffering) break; if(rts.GetSegment(iSegment)->subs.GetSize() == 0) continue; int start = rts.TranslateSegmentStart(iSegment, m_fps); int stop = rts.TranslateSegmentEnd(iSegment, m_fps); if(PTS < stop) { AddText(cur, start, stop, PTS); PTS = stop; } } } m_sic.MarkAsEnd(PTS); } } break; default: DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_UNKNOWN"))); Reply(E_FAIL); return -1; } m_fThreadActive = false; if(m_fBreakBuffering) m_sic.RemoveAll(); m_fBreakBuffering = false; } return 1; } //////////////////////////////////////////////////////////////// CDirectVobSubFilter::SubImageCache::SubImageCache() { hdc = 0; hfont = 0; memset(imgs, 0, sizeof(SubImage)*MAXSUBIMAGECACHE); pos = size = start = stop = 0; } CDirectVobSubFilter::SubImageCache::~SubImageCache() { DeInit(); } void CDirectVobSubFilter::SubImageCache::Init(int w, int h) { CAutoLock cAutoLock(&m_csAccessLock); DeInit(); h = abs(h); this->w = w; this->h = h; hdc = CreateCompatibleDC(NULL); for(int i = 0; i < MAXSUBIMAGECACHE; i++) { struct BM { BITMAPINFOHEADER bih; DWORD mask[3]; } b; memset(&b, 0, sizeof(b)); b.bih.biSize = sizeof(BITMAPV4HEADER); b.bih.biWidth = w; b.bih.biHeight = h; b.bih.biBitCount = 32; b.bih.biPlanes = 1; b.bih.biCompression = BI_BITFIELDS; b.mask[0] = 0xFF0000; b.mask[1] = 0x00FF00; b.mask[2] = 0x0000FF; imgs[i].hbm = CreateDIBSection( hdc, (BITMAPINFO *)&b, DIB_RGB_COLORS, NULL, NULL, 0); } } void CDirectVobSubFilter::SubImageCache::DeInit() { CAutoLock cAutoLock(&m_csAccessLock); for(int i = 0; i < MAXSUBIMAGECACHE; i++) { if(imgs[i].hbm) {DeleteObject(imgs[i].hbm); imgs[i].hbm = 0;} } if(hfont) {DeleteObject(hfont); hfont = 0;} if(hdc) {DeleteDC(hdc); hdc = 0;} memset(imgs, 0, sizeof(SubImage)*MAXSUBIMAGECACHE); RemoveAll(); } void CDirectVobSubFilter::SubImageCache::SetFont(LOGFONT* lf) { CAutoLock cAutoLock(&m_csAccessLock); if(hfont) DeleteObject(hfont); hfont = CreateFontIndirect(lf); } void CDirectVobSubFilter::SubImageCache::RemoveOldest() { CAutoLock cAutoLock(&m_csAccessLock); if(size <= 0) return; DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: removing no.%d"), pos)); start = imgs[pos].stop; pos = (pos + 1) % MAXSUBIMAGECACHE; size--; if(size <= 0) stop = start; } void CDirectVobSubFilter::SubImageCache::RemoveAll() { CAutoLock cAutoLock(&m_csAccessLock); if(size > 0) DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: removing all"))); pos = size = 0; start = INT_MAX; stop = -1; } void CDirectVobSubFilter::SubImageCache::Update(int PTS) { CAutoLock cAutoLock(&m_csAccessLock); if(PTS < start) { RemoveAll(); start = stop = PTS; } else if(size >= MAXSUBIMAGECACHE) { for(int i = pos, j = i + size; i < j; i++) { int k = i % MAXSUBIMAGECACHE; if(imgs[k].stop <= PTS) { RemoveOldest(); break; // looks more effective this way } } } } void CDirectVobSubFilter::SubImageCache::FreeExtra(int PTS) { CAutoLock cAutoLock(&m_csAccessLock); if(PTS < start) { RemoveAll(); } else if(size >= 0) { for(int i = pos, j = i + size; i < j; i++) { int k = i % MAXSUBIMAGECACHE; if(imgs[k].start >= PTS) { DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: removing extra %d+ above %d"), i, PTS)); size = i - pos; break; } } } DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: adjusting end to %d"), PTS)); start = min(start, PTS); stop = min(stop, PTS); } SubImage* CDirectVobSubFilter::SubImageCache::GetSubImage(int i) { CAutoLock cAutoLock(&m_csAccessLock); return(size > 0 && i >= 0 && i < size ? &imgs[(pos + i) % MAXSUBIMAGECACHE] : NULL); } SubImage* CDirectVobSubFilter::SubImageCache::GetLast() { CAutoLock cAutoLock(&m_csAccessLock); return(size > 0 ? &imgs[(pos + size - 1) % MAXSUBIMAGECACHE] : NULL); } SubImage* CDirectVobSubFilter::SubImageCache::GetNextFree() { CAutoLock cAutoLock(&m_csAccessLock); if(size < MAXSUBIMAGECACHE) DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: offering no.%d"), (pos + size) % MAXSUBIMAGECACHE)); return(size < MAXSUBIMAGECACHE ? &imgs[(pos + size) % MAXSUBIMAGECACHE] : NULL); } void CDirectVobSubFilter::SubImageCache::MarkNextAsOccupied() { CAutoLock cAutoLock(&m_csAccessLock); if(size < MAXSUBIMAGECACHE) { SubImage* next = &imgs[(pos + size) % MAXSUBIMAGECACHE]; DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: adding no.%d (%d - %d)"), (pos + size) % MAXSUBIMAGECACHE, next->start, next->stop)); MarkAsEnd(next->stop); size++; } } SubImage* CDirectVobSubFilter::SubImageCache::LookupSubImage(int PTS) { CAutoLock cAutoLock(&m_csAccessLock); SubImage* ret = NULL; for(int i = pos, j = i + size; i < j; i++) { int k = i % MAXSUBIMAGECACHE; if(imgs[k].start <= PTS && PTS < imgs[k].stop) { ret = &imgs[k]; break; } } return(ret); } bool CDirectVobSubFilter::SubImageCache::CachedEnough() { CAutoLock cAutoLock(&m_csAccessLock); return(CachedEnough(start)); } bool CDirectVobSubFilter::SubImageCache::CachedEnough(int PTS) { CAutoLock cAutoLock(&m_csAccessLock); return((stop - PTS) > LOOKAHEADSIZE || size == MAXSUBIMAGECACHE); } void CDirectVobSubFilter::SubImageCache::MarkAsStart(int PTS) { CAutoLock cAutoLock(&m_csAccessLock); start = min(start, PTS); } void CDirectVobSubFilter::SubImageCache::MarkAsEnd(int PTS) { CAutoLock cAutoLock(&m_csAccessLock); stop = max(stop, PTS); } int CDirectVobSubFilter::SubImageCache::SeekToEnd(int PTS) { CAutoLock cAutoLock(&m_csAccessLock); return(max(stop, PTS)); } void CDirectVobSubFilter::SubImageCache::GetStats(int& start, int& stop, int& pos, int& size) { CAutoLock cAutoLock(&m_csAccessLock); start = this->start; stop = this->stop; pos = this->pos; size = this->size; } //// void SubImage::Clear(uint c) { BITMAP bm; GetObject(hbm, sizeof(bm), &bm); memsetd(bm.bmBits, c, bm.bmWidthBytes*bm.bmHeight); } void SubImage::ConvertToYUY2() { BITMAP bm; GetObject(hbm, sizeof(bm), &bm); for(int y = 0; y < bm.bmHeight; y++) { uchar* sub = (uchar*)bm.bmBits + bm.bmWidthBytes*y; uchar* end = sub + bm.bmWidth*4; for(; sub < end; sub += 8) { int a3 = (sub[3]+sub[7])>>1; if(a3 < 0xff) { sub[1] = (c2y_yb[sub[0]] + c2y_yg[sub[1]] + c2y_yr[sub[2]] + 0x108000) >> 16; sub[5] = (c2y_yb[sub[4]] + c2y_yg[sub[5]] + c2y_yr[sub[6]] + 0x108000) >> 16; int scaled_y = (sub[1]+sub[5]-32) * cy_cy2; sub[0] = Clip[(((((sub[0]+sub[4])<<15) - scaled_y) >> 10) * c2y_cu + 0x800000 + 0x8000) >> 16]; sub[4] = Clip[(((((sub[2]+sub[6])<<15) - scaled_y) >> 10) * c2y_cv + 0x800000 + 0x8000) >> 16]; } else { sub[1] = sub[5] = 0x10; sub[0] = sub[4] = 0x80; } } } } //////////////////////////////////////////////////////////////// void CDirectVobSubFilter::SetupFRD(CFileReloaderData* frd, CStringArray& paths, CArray & handles) { CAutoLock cAutolock(&frd->pFilter->m_fileLock); for(int i = 2; i < handles.GetSize(); i++) { FindCloseChangeNotification(handles[i]); } paths.RemoveAll(); handles.RemoveAll(); handles.Add(frd->hEndThreadEvent); handles.Add(frd->hRefreshEvent); frd->mtime.SetSize(frd->files.GetSize()); for(i = 0; i < frd->files.GetSize(); i++) { CFileStatus status; if(CFile::GetStatus(frd->files[i], status)) frd->mtime[i] = status.m_mtime; CString fn = frd->files[i]; fn.Replace('\\', '/'); fn = fn.Left(fn.ReverseFind('/')+1); bool fFound = false; for(int j = 0; !fFound && j < paths.GetSize(); j++) { if(paths[j] == fn) fFound = true; } if(!fFound) { paths.Add(fn); HANDLE h = FindFirstChangeNotification(fn, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE); if(h != INVALID_HANDLE_VALUE) handles.Add(h); } } } DWORD WINAPI CDirectVobSubFilter::FileReloaderThreadProc(LPVOID lpParameter) { CFileReloaderData* frd = (CFileReloaderData*)lpParameter; CStringArray paths; CArray handles; SetupFRD(frd, paths, handles); while(1) { DWORD idx = WaitForMultipleObjects(handles.GetSize(), handles.GetData(), FALSE, INFINITE); if(idx == (WAIT_OBJECT_0 + 0)) // frd->hEndThreadEvent { break; } if(idx == (WAIT_OBJECT_0 + 1)) // frd->hRefreshEvent { SetupFRD(frd, paths, handles); } else if(idx >= (WAIT_OBJECT_0 + 2) && idx < (WAIT_OBJECT_0 + handles.GetSize())) { bool fLocked = true; frd->pFilter->IsSubtitleReloaderLocked(&fLocked); if(fLocked) continue; if(FindNextChangeNotification(handles[idx - WAIT_OBJECT_0]) == FALSE) break; int j = 0; for(int i = 0; i < frd->files.GetSize() && j == 0; i++) { CFileStatus status; if(CFile::GetStatus(frd->files[i], status) && frd->mtime[i] != status.m_mtime) { for(j = 0; j < 10; j++) { FILE* f = _tfopen(frd->files[i], _T("rb+")); if(f) { fclose(f); j = 0; break; } else { Sleep(100); j++; } } } } if(j > 0) { SetupFRD(frd, paths, handles); } else { Sleep(500); for(i = 0; i < frd->files.GetSize(); i++) { CFileStatus status; if(CFile::GetStatus(frd->files[i], status) && frd->mtime[i] != status.m_mtime) { bool fTemp = frd->pFilter->m_fHideSubtitles; frd->pFilter->Open(); frd->pFilter->m_fHideSubtitles = fTemp; SetupFRD(frd, paths, handles); break; } } } } else { break; } } for(int i = 2; i < handles.GetSize(); i++) { FindCloseChangeNotification(handles[i]); } return(0); }