mirror of https://github.com/odrling/Aegisub
2219 lines
55 KiB
C++
2219 lines
55 KiB
C++
|
/*
|
|||
|
* Copyright (C) 2003-2006 Gabest
|
|||
|
* http://www.gabest.org
|
|||
|
*
|
|||
|
* This Program is free software; you can redistribute it and/or modify
|
|||
|
* it under the terms of the GNU General Public License as published by
|
|||
|
* the Free Software Foundation; either version 2, or (at your option)
|
|||
|
* any later version.
|
|||
|
*
|
|||
|
* This Program is distributed in the hope that it will be useful,
|
|||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
* GNU General Public License for more details.
|
|||
|
*
|
|||
|
* You should have received a copy of the GNU General Public License
|
|||
|
* along with GNU Make; see the file COPYING. If not, write to
|
|||
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|||
|
* http://www.gnu.org/copyleft/gpl.html
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
#include "stdafx.h"
|
|||
|
#include <Vfw.h>
|
|||
|
#include "../include/winddk/devioctl.h"
|
|||
|
#include "../include/winddk/ntddcdrm.h"
|
|||
|
#include "DSUtil.h"
|
|||
|
#include "../include/moreuuids.h"
|
|||
|
|
|||
|
void DumpStreamConfig(TCHAR* fn, IAMStreamConfig* pAMVSCCap)
|
|||
|
{
|
|||
|
CString s, ss;
|
|||
|
CStdioFile f;
|
|||
|
if(!f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeText))
|
|||
|
return;
|
|||
|
|
|||
|
int cnt = 0, size = 0;
|
|||
|
if(FAILED(pAMVSCCap->GetNumberOfCapabilities(&cnt, &size)))
|
|||
|
return;
|
|||
|
|
|||
|
s.Empty();
|
|||
|
s.Format(_T("cnt %d, size %d\n"), cnt, size);
|
|||
|
f.WriteString(s);
|
|||
|
|
|||
|
if(size == sizeof(VIDEO_STREAM_CONFIG_CAPS))
|
|||
|
{
|
|||
|
for(int i = 0; i < cnt; i++)
|
|||
|
{
|
|||
|
AM_MEDIA_TYPE* pmt = NULL;
|
|||
|
|
|||
|
VIDEO_STREAM_CONFIG_CAPS caps;
|
|||
|
memset(&caps, 0, sizeof(caps));
|
|||
|
|
|||
|
s.Empty();
|
|||
|
ss.Format(_T("%d\n"), i); s += ss;
|
|||
|
f.WriteString(s);
|
|||
|
|
|||
|
if(FAILED(pAMVSCCap->GetStreamCaps(i, &pmt, (BYTE*)&caps)))
|
|||
|
continue;
|
|||
|
|
|||
|
{
|
|||
|
s.Empty();
|
|||
|
ss.Format(_T("VIDEO_STREAM_CONFIG_CAPS\n")); s += ss;
|
|||
|
ss.Format(_T("\tVideoStandard 0x%08x\n"), caps.VideoStandard); s += ss;
|
|||
|
ss.Format(_T("\tInputSize %dx%d\n"), caps.InputSize); s += ss;
|
|||
|
ss.Format(_T("\tCroppingSize %dx%d - %dx%d\n"), caps.MinCroppingSize, caps.MaxCroppingSize); s += ss;
|
|||
|
ss.Format(_T("\tCropGranularity %d, %d\n"), caps.CropGranularityX, caps.CropGranularityY); s += ss;
|
|||
|
ss.Format(_T("\tCropAlign %d, %d\n"), caps.CropAlignX, caps.CropAlignY); s += ss;
|
|||
|
ss.Format(_T("\tOutputSize %dx%d - %dx%d\n"), caps.MinOutputSize, caps.MaxOutputSize); s += ss;
|
|||
|
ss.Format(_T("\tOutputGranularity %d, %d\n"), caps.OutputGranularityX, caps.OutputGranularityY); s += ss;
|
|||
|
ss.Format(_T("\tStretchTaps %d, %d\n"), caps.StretchTapsX, caps.StretchTapsY); s += ss;
|
|||
|
ss.Format(_T("\tShrinkTaps %d, %d\n"), caps.ShrinkTapsX, caps.ShrinkTapsY); s += ss;
|
|||
|
ss.Format(_T("\tFrameInterval %I64d, %I64d (%.4f, %.4f)\n"),
|
|||
|
caps.MinFrameInterval, caps.MaxFrameInterval,
|
|||
|
(float)10000000/caps.MinFrameInterval, (float)10000000/caps.MaxFrameInterval); s += ss;
|
|||
|
ss.Format(_T("\tBitsPerSecond %d - %d\n"), caps.MinBitsPerSecond, caps.MaxBitsPerSecond); s += ss;
|
|||
|
f.WriteString(s);
|
|||
|
}
|
|||
|
|
|||
|
BITMAPINFOHEADER* pbh;
|
|||
|
if(pmt->formattype == FORMAT_VideoInfo)
|
|||
|
{
|
|||
|
VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->pbFormat;
|
|||
|
pbh = &vih->bmiHeader;
|
|||
|
|
|||
|
s.Empty();
|
|||
|
ss.Format(_T("FORMAT_VideoInfo\n")); s += ss;
|
|||
|
ss.Format(_T("\tAvgTimePerFrame %I64d, %.4f\n"), vih->AvgTimePerFrame, (float)10000000/vih->AvgTimePerFrame); s += ss;
|
|||
|
ss.Format(_T("\trcSource %d,%d,%d,%d\n"), vih->rcSource); s += ss;
|
|||
|
ss.Format(_T("\trcTarget %d,%d,%d,%d\n"), vih->rcTarget); s += ss;
|
|||
|
f.WriteString(s);
|
|||
|
}
|
|||
|
else if(pmt->formattype == FORMAT_VideoInfo2)
|
|||
|
{
|
|||
|
VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->pbFormat;
|
|||
|
pbh = &vih->bmiHeader;
|
|||
|
|
|||
|
s.Empty();
|
|||
|
ss.Format(_T("FORMAT_VideoInfo2\n")); s += ss;
|
|||
|
ss.Format(_T("\tAvgTimePerFrame %I64d, %.4f\n"), vih->AvgTimePerFrame, (float)10000000/vih->AvgTimePerFrame); s += ss;
|
|||
|
ss.Format(_T("\trcSource %d,%d,%d,%d\n"), vih->rcSource); s += ss;
|
|||
|
ss.Format(_T("\trcTarget %d,%d,%d,%d\n"), vih->rcTarget); s += ss;
|
|||
|
ss.Format(_T("\tdwInterlaceFlags 0x%x\n"), vih->dwInterlaceFlags); s += ss;
|
|||
|
ss.Format(_T("\tdwPictAspectRatio %d:%d\n"), vih->dwPictAspectRatioX, vih->dwPictAspectRatioY); s += ss;
|
|||
|
f.WriteString(s);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DeleteMediaType(pmt);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
s.Empty();
|
|||
|
ss.Format(_T("BITMAPINFOHEADER\n")); s += ss;
|
|||
|
ss.Format(_T("\tbiCompression %x\n"), pbh->biCompression); s += ss;
|
|||
|
ss.Format(_T("\tbiWidth %d\n"), pbh->biWidth); s += ss;
|
|||
|
ss.Format(_T("\tbiHeight %d\n"), pbh->biHeight); s += ss;
|
|||
|
ss.Format(_T("\tbiBitCount %d\n"), pbh->biBitCount); s += ss;
|
|||
|
ss.Format(_T("\tbiPlanes %d\n"), pbh->biPlanes); s += ss;
|
|||
|
ss.Format(_T("\tbiSizeImage %d\n"), pbh->biSizeImage); s += ss;
|
|||
|
f.WriteString(s);
|
|||
|
|
|||
|
DeleteMediaType(pmt);
|
|||
|
}
|
|||
|
}
|
|||
|
else if(size == sizeof(AUDIO_STREAM_CONFIG_CAPS))
|
|||
|
{
|
|||
|
// TODO
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int CountPins(IBaseFilter* pBF, int& nIn, int& nOut, int& nInC, int& nOutC)
|
|||
|
{
|
|||
|
nIn = nOut = 0;
|
|||
|
nInC = nOutC = 0;
|
|||
|
|
|||
|
BeginEnumPins(pBF, pEP, pPin)
|
|||
|
{
|
|||
|
PIN_DIRECTION dir;
|
|||
|
if(SUCCEEDED(pPin->QueryDirection(&dir)))
|
|||
|
{
|
|||
|
CComPtr<IPin> pPinConnectedTo;
|
|||
|
pPin->ConnectedTo(&pPinConnectedTo);
|
|||
|
|
|||
|
if(dir == PINDIR_INPUT) {nIn++; if(pPinConnectedTo) nInC++;}
|
|||
|
else if(dir == PINDIR_OUTPUT) {nOut++; if(pPinConnectedTo) nOutC++;}
|
|||
|
}
|
|||
|
}
|
|||
|
EndEnumPins
|
|||
|
|
|||
|
return(nIn+nOut);
|
|||
|
}
|
|||
|
|
|||
|
bool IsSplitter(IBaseFilter* pBF, bool fCountConnectedOnly)
|
|||
|
{
|
|||
|
int nIn, nOut, nInC, nOutC;
|
|||
|
CountPins(pBF, nIn, nOut, nInC, nOutC);
|
|||
|
return(fCountConnectedOnly ? nOutC > 1 : nOut > 1);
|
|||
|
}
|
|||
|
|
|||
|
bool IsMultiplexer(IBaseFilter* pBF, bool fCountConnectedOnly)
|
|||
|
{
|
|||
|
int nIn, nOut, nInC, nOutC;
|
|||
|
CountPins(pBF, nIn, nOut, nInC, nOutC);
|
|||
|
return(fCountConnectedOnly ? nInC > 1 : nIn > 1);
|
|||
|
}
|
|||
|
|
|||
|
bool IsStreamStart(IBaseFilter* pBF)
|
|||
|
{
|
|||
|
CComQIPtr<IAMFilterMiscFlags> pAMMF(pBF);
|
|||
|
if(pAMMF && pAMMF->GetMiscFlags()&AM_FILTER_MISC_FLAGS_IS_SOURCE)
|
|||
|
return(true);
|
|||
|
|
|||
|
int nIn, nOut, nInC, nOutC;
|
|||
|
CountPins(pBF, nIn, nOut, nInC, nOutC);
|
|||
|
AM_MEDIA_TYPE mt;
|
|||
|
CComPtr<IPin> pIn = GetFirstPin(pBF);
|
|||
|
return((nOut > 1)
|
|||
|
|| (nOut > 0 && nIn == 1 && pIn && SUCCEEDED(pIn->ConnectionMediaType(&mt)) && mt.majortype == MEDIATYPE_Stream));
|
|||
|
}
|
|||
|
|
|||
|
bool IsStreamEnd(IBaseFilter* pBF)
|
|||
|
{
|
|||
|
int nIn, nOut, nInC, nOutC;
|
|||
|
CountPins(pBF, nIn, nOut, nInC, nOutC);
|
|||
|
return(nOut == 0);
|
|||
|
}
|
|||
|
|
|||
|
bool IsVideoRenderer(IBaseFilter* pBF)
|
|||
|
{
|
|||
|
int nIn, nOut, nInC, nOutC;
|
|||
|
CountPins(pBF, nIn, nOut, nInC, nOutC);
|
|||
|
|
|||
|
if(nInC > 0 && nOut == 0)
|
|||
|
{
|
|||
|
BeginEnumPins(pBF, pEP, pPin)
|
|||
|
{
|
|||
|
AM_MEDIA_TYPE mt;
|
|||
|
if(S_OK != pPin->ConnectionMediaType(&mt))
|
|||
|
continue;
|
|||
|
|
|||
|
FreeMediaType(mt);
|
|||
|
|
|||
|
return(!!(mt.majortype == MEDIATYPE_Video));
|
|||
|
/*&& (mt.formattype == FORMAT_VideoInfo || mt.formattype == FORMAT_VideoInfo2));*/
|
|||
|
}
|
|||
|
EndEnumPins
|
|||
|
}
|
|||
|
|
|||
|
CLSID clsid;
|
|||
|
memcpy(&clsid, &GUID_NULL, sizeof(clsid));
|
|||
|
pBF->GetClassID(&clsid);
|
|||
|
|
|||
|
return(clsid == CLSID_VideoRenderer || clsid == CLSID_VideoRendererDefault);
|
|||
|
}
|
|||
|
|
|||
|
#include <initguid.h>
|
|||
|
|
|||
|
DEFINE_GUID(CLSID_ReClock,
|
|||
|
0x9dc15360, 0x914c, 0x46b8, 0xb9, 0xdf, 0xbf, 0xe6, 0x7f, 0xd3, 0x6c, 0x6a);
|
|||
|
|
|||
|
bool IsAudioWaveRenderer(IBaseFilter* pBF)
|
|||
|
{
|
|||
|
int nIn, nOut, nInC, nOutC;
|
|||
|
CountPins(pBF, nIn, nOut, nInC, nOutC);
|
|||
|
|
|||
|
if(nInC > 0 && nOut == 0 && CComQIPtr<IBasicAudio>(pBF))
|
|||
|
{
|
|||
|
BeginEnumPins(pBF, pEP, pPin)
|
|||
|
{
|
|||
|
AM_MEDIA_TYPE mt;
|
|||
|
if(S_OK != pPin->ConnectionMediaType(&mt))
|
|||
|
continue;
|
|||
|
|
|||
|
FreeMediaType(mt);
|
|||
|
|
|||
|
return(!!(mt.majortype == MEDIATYPE_Audio)
|
|||
|
/*&& mt.formattype == FORMAT_WaveFormatEx*/);
|
|||
|
}
|
|||
|
EndEnumPins
|
|||
|
}
|
|||
|
|
|||
|
CLSID clsid;
|
|||
|
memcpy(&clsid, &GUID_NULL, sizeof(clsid));
|
|||
|
pBF->GetClassID(&clsid);
|
|||
|
|
|||
|
return(clsid == CLSID_DSoundRender || clsid == CLSID_AudioRender || clsid == CLSID_ReClock
|
|||
|
|| clsid == __uuidof(CNullAudioRenderer) || clsid == __uuidof(CNullUAudioRenderer));
|
|||
|
}
|
|||
|
|
|||
|
IBaseFilter* GetUpStreamFilter(IBaseFilter* pBF, IPin* pInputPin)
|
|||
|
{
|
|||
|
return GetFilterFromPin(GetUpStreamPin(pBF, pInputPin));
|
|||
|
}
|
|||
|
|
|||
|
IPin* GetUpStreamPin(IBaseFilter* pBF, IPin* pInputPin)
|
|||
|
{
|
|||
|
BeginEnumPins(pBF, pEP, pPin)
|
|||
|
{
|
|||
|
if(pInputPin && pInputPin != pPin) continue;
|
|||
|
|
|||
|
PIN_DIRECTION dir;
|
|||
|
CComPtr<IPin> pPinConnectedTo;
|
|||
|
if(SUCCEEDED(pPin->QueryDirection(&dir)) && dir == PINDIR_INPUT
|
|||
|
&& SUCCEEDED(pPin->ConnectedTo(&pPinConnectedTo)))
|
|||
|
{
|
|||
|
IPin* pRet = pPinConnectedTo.Detach();
|
|||
|
pRet->Release();
|
|||
|
return(pRet);
|
|||
|
}
|
|||
|
}
|
|||
|
EndEnumPins
|
|||
|
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
|
|||
|
IPin* GetFirstPin(IBaseFilter* pBF, PIN_DIRECTION dir)
|
|||
|
{
|
|||
|
if(!pBF) return(NULL);
|
|||
|
|
|||
|
BeginEnumPins(pBF, pEP, pPin)
|
|||
|
{
|
|||
|
PIN_DIRECTION dir2;
|
|||
|
pPin->QueryDirection(&dir2);
|
|||
|
if(dir == dir2)
|
|||
|
{
|
|||
|
IPin* pRet = pPin.Detach();
|
|||
|
pRet->Release();
|
|||
|
return(pRet);
|
|||
|
}
|
|||
|
}
|
|||
|
EndEnumPins
|
|||
|
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
|
|||
|
IPin* GetFirstDisconnectedPin(IBaseFilter* pBF, PIN_DIRECTION dir)
|
|||
|
{
|
|||
|
if(!pBF) return(NULL);
|
|||
|
|
|||
|
BeginEnumPins(pBF, pEP, pPin)
|
|||
|
{
|
|||
|
PIN_DIRECTION dir2;
|
|||
|
pPin->QueryDirection(&dir2);
|
|||
|
CComPtr<IPin> pPinTo;
|
|||
|
if(dir == dir2 && (S_OK != pPin->ConnectedTo(&pPinTo)))
|
|||
|
{
|
|||
|
IPin* pRet = pPin.Detach();
|
|||
|
pRet->Release();
|
|||
|
return(pRet);
|
|||
|
}
|
|||
|
}
|
|||
|
EndEnumPins
|
|||
|
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
|
|||
|
IBaseFilter* FindFilter(LPCWSTR clsid, IFilterGraph* pFG)
|
|||
|
{
|
|||
|
CLSID clsid2;
|
|||
|
CLSIDFromString(CComBSTR(clsid), &clsid2);
|
|||
|
return(FindFilter(clsid2, pFG));
|
|||
|
}
|
|||
|
|
|||
|
IBaseFilter* FindFilter(const CLSID& clsid, IFilterGraph* pFG)
|
|||
|
{
|
|||
|
BeginEnumFilters(pFG, pEF, pBF)
|
|||
|
{
|
|||
|
CLSID clsid2;
|
|||
|
if(SUCCEEDED(pBF->GetClassID(&clsid2)) && clsid == clsid2)
|
|||
|
return(pBF);
|
|||
|
}
|
|||
|
EndEnumFilters
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
CStringW GetFilterName(IBaseFilter* pBF)
|
|||
|
{
|
|||
|
CStringW name;
|
|||
|
CFilterInfo fi;
|
|||
|
if(pBF && SUCCEEDED(pBF->QueryFilterInfo(&fi)))
|
|||
|
name = fi.achName;
|
|||
|
return(name);
|
|||
|
}
|
|||
|
|
|||
|
CStringW GetPinName(IPin* pPin)
|
|||
|
{
|
|||
|
CStringW name;
|
|||
|
CPinInfo pi;
|
|||
|
if(pPin && SUCCEEDED(pPin->QueryPinInfo(&pi)))
|
|||
|
name = pi.achName;
|
|||
|
return(name);
|
|||
|
}
|
|||
|
|
|||
|
IFilterGraph* GetGraphFromFilter(IBaseFilter* pBF)
|
|||
|
{
|
|||
|
if(!pBF) return NULL;
|
|||
|
IFilterGraph* pGraph = NULL;
|
|||
|
CFilterInfo fi;
|
|||
|
if(pBF && SUCCEEDED(pBF->QueryFilterInfo(&fi)))
|
|||
|
pGraph = fi.pGraph;
|
|||
|
return(pGraph);
|
|||
|
}
|
|||
|
|
|||
|
IBaseFilter* GetFilterFromPin(IPin* pPin)
|
|||
|
{
|
|||
|
if(!pPin) return NULL;
|
|||
|
IBaseFilter* pBF = NULL;
|
|||
|
CPinInfo pi;
|
|||
|
if(pPin && SUCCEEDED(pPin->QueryPinInfo(&pi)))
|
|||
|
pBF = pi.pFilter;
|
|||
|
return(pBF);
|
|||
|
}
|
|||
|
|
|||
|
IPin* AppendFilter(IPin* pPin, CString DisplayName, IGraphBuilder* pGB)
|
|||
|
{
|
|||
|
IPin* pRet = pPin;
|
|||
|
|
|||
|
CInterfaceList<IBaseFilter> pFilters;
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
if(!pPin || DisplayName.IsEmpty() || !pGB)
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IPin> pPinTo;
|
|||
|
PIN_DIRECTION dir;
|
|||
|
if(FAILED(pPin->QueryDirection(&dir)) || dir != PINDIR_OUTPUT || SUCCEEDED(pPin->ConnectedTo(&pPinTo)))
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IBindCtx> pBindCtx;
|
|||
|
CreateBindCtx(0, &pBindCtx);
|
|||
|
|
|||
|
CComPtr<IMoniker> pMoniker;
|
|||
|
ULONG chEaten;
|
|||
|
if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IBaseFilter> pBF;
|
|||
|
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IPropertyBag> pPB;
|
|||
|
if(FAILED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB)))
|
|||
|
break;
|
|||
|
|
|||
|
CComVariant var;
|
|||
|
if(FAILED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
|
|||
|
break;
|
|||
|
|
|||
|
pFilters.AddTail(pBF);
|
|||
|
BeginEnumFilters(pGB, pEnum, pBF2)
|
|||
|
pFilters.AddTail(pBF2);
|
|||
|
EndEnumFilters
|
|||
|
|
|||
|
if(FAILED(pGB->AddFilter(pBF, CStringW(var.bstrVal))))
|
|||
|
break;
|
|||
|
|
|||
|
BeginEnumFilters(pGB, pEnum, pBF2)
|
|||
|
if(!pFilters.Find(pBF2) && SUCCEEDED(pGB->RemoveFilter(pBF2)))
|
|||
|
pEnum->Reset();
|
|||
|
EndEnumFilters
|
|||
|
|
|||
|
pPinTo = GetFirstPin(pBF, PINDIR_INPUT);
|
|||
|
if(!pPinTo)
|
|||
|
{
|
|||
|
pGB->RemoveFilter(pBF);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
HRESULT hr;
|
|||
|
if(FAILED(hr = pGB->ConnectDirect(pPin, pPinTo, NULL)))
|
|||
|
{
|
|||
|
hr = pGB->Connect(pPin, pPinTo);
|
|||
|
pGB->RemoveFilter(pBF);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
BeginEnumFilters(pGB, pEnum, pBF2)
|
|||
|
if(!pFilters.Find(pBF2) && SUCCEEDED(pGB->RemoveFilter(pBF2)))
|
|||
|
pEnum->Reset();
|
|||
|
EndEnumFilters
|
|||
|
|
|||
|
pRet = GetFirstPin(pBF, PINDIR_OUTPUT);
|
|||
|
if(!pRet)
|
|||
|
{
|
|||
|
pRet = pPin;
|
|||
|
pGB->RemoveFilter(pBF);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
while(false);
|
|||
|
|
|||
|
return(pRet);
|
|||
|
}
|
|||
|
|
|||
|
IPin* InsertFilter(IPin* pPin, CString DisplayName, IGraphBuilder* pGB)
|
|||
|
{
|
|||
|
do
|
|||
|
{
|
|||
|
if(!pPin || DisplayName.IsEmpty() || !pGB)
|
|||
|
break;
|
|||
|
|
|||
|
PIN_DIRECTION dir;
|
|||
|
if(FAILED(pPin->QueryDirection(&dir)))
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IPin> pFrom, pTo;
|
|||
|
|
|||
|
if(dir == PINDIR_INPUT)
|
|||
|
{
|
|||
|
pPin->ConnectedTo(&pFrom);
|
|||
|
pTo = pPin;
|
|||
|
}
|
|||
|
else if(dir == PINDIR_OUTPUT)
|
|||
|
{
|
|||
|
pFrom = pPin;
|
|||
|
pPin->ConnectedTo(&pTo);
|
|||
|
}
|
|||
|
|
|||
|
if(!pFrom || !pTo)
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IBindCtx> pBindCtx;
|
|||
|
CreateBindCtx(0, &pBindCtx);
|
|||
|
|
|||
|
CComPtr<IMoniker> pMoniker;
|
|||
|
ULONG chEaten;
|
|||
|
if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IBaseFilter> pBF;
|
|||
|
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IPropertyBag> pPB;
|
|||
|
if(FAILED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB)))
|
|||
|
break;
|
|||
|
|
|||
|
CComVariant var;
|
|||
|
if(FAILED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
|
|||
|
break;
|
|||
|
|
|||
|
if(FAILED(pGB->AddFilter(pBF, CStringW(var.bstrVal))))
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IPin> pFromTo = GetFirstPin(pBF, PINDIR_INPUT);
|
|||
|
if(!pFromTo)
|
|||
|
{
|
|||
|
pGB->RemoveFilter(pBF);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if(FAILED(pGB->Disconnect(pFrom)) || FAILED(pGB->Disconnect(pTo)))
|
|||
|
{
|
|||
|
pGB->RemoveFilter(pBF);
|
|||
|
pGB->ConnectDirect(pFrom, pTo, NULL);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
HRESULT hr;
|
|||
|
if(FAILED(hr = pGB->ConnectDirect(pFrom, pFromTo, NULL)))
|
|||
|
{
|
|||
|
pGB->RemoveFilter(pBF);
|
|||
|
pGB->ConnectDirect(pFrom, pTo, NULL);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
CComPtr<IPin> pToFrom = GetFirstPin(pBF, PINDIR_OUTPUT);
|
|||
|
if(!pToFrom)
|
|||
|
{
|
|||
|
pGB->RemoveFilter(pBF);
|
|||
|
pGB->ConnectDirect(pFrom, pTo, NULL);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if(FAILED(pGB->ConnectDirect(pToFrom, pTo, NULL)))
|
|||
|
{
|
|||
|
pGB->RemoveFilter(pBF);
|
|||
|
pGB->ConnectDirect(pFrom, pTo, NULL);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
pPin = pToFrom;
|
|||
|
}
|
|||
|
while(false);
|
|||
|
|
|||
|
return(pPin);
|
|||
|
}
|
|||
|
|
|||
|
void ExtractMediaTypes(IPin* pPin, CAtlArray<GUID>& types)
|
|||
|
{
|
|||
|
types.RemoveAll();
|
|||
|
|
|||
|
BeginEnumMediaTypes(pPin, pEM, pmt)
|
|||
|
{
|
|||
|
bool fFound = false;
|
|||
|
|
|||
|
for(int i = 0; !fFound && i < types.GetCount(); i += 2)
|
|||
|
{
|
|||
|
if(types[i] == pmt->majortype && types[i+1] == pmt->subtype)
|
|||
|
fFound = true;
|
|||
|
}
|
|||
|
|
|||
|
if(!fFound)
|
|||
|
{
|
|||
|
types.Add(pmt->majortype);
|
|||
|
types.Add(pmt->subtype);
|
|||
|
}
|
|||
|
}
|
|||
|
EndEnumMediaTypes(pmt)
|
|||
|
}
|
|||
|
|
|||
|
void ExtractMediaTypes(IPin* pPin, CAtlList<CMediaType>& mts)
|
|||
|
{
|
|||
|
mts.RemoveAll();
|
|||
|
|
|||
|
BeginEnumMediaTypes(pPin, pEM, pmt)
|
|||
|
{
|
|||
|
bool fFound = false;
|
|||
|
|
|||
|
POSITION pos = mts.GetHeadPosition();
|
|||
|
while(!fFound && pos)
|
|||
|
{
|
|||
|
CMediaType& mt = mts.GetNext(pos);
|
|||
|
if(mt.majortype == pmt->majortype && mt.subtype == pmt->subtype)
|
|||
|
fFound = true;
|
|||
|
}
|
|||
|
|
|||
|
if(!fFound)
|
|||
|
{
|
|||
|
mts.AddTail(CMediaType(*pmt));
|
|||
|
}
|
|||
|
}
|
|||
|
EndEnumMediaTypes(pmt)
|
|||
|
}
|
|||
|
|
|||
|
int Eval_Exception(int n_except)
|
|||
|
{
|
|||
|
if(n_except == STATUS_ACCESS_VIOLATION)
|
|||
|
{
|
|||
|
AfxMessageBox(_T("The property page of this filter has just caused a\nmemory access violation. The application will gently die now :)"));
|
|||
|
}
|
|||
|
|
|||
|
return EXCEPTION_CONTINUE_SEARCH;
|
|||
|
}
|
|||
|
|
|||
|
void MyOleCreatePropertyFrame(HWND hwndOwner, UINT x, UINT y, LPCOLESTR lpszCaption, ULONG cObjects, LPUNKNOWN FAR* lplpUnk, ULONG cPages, LPCLSID lpPageClsID, LCID lcid, DWORD dwReserved, LPVOID lpvReserved)
|
|||
|
{
|
|||
|
__try
|
|||
|
{
|
|||
|
OleCreatePropertyFrame(hwndOwner, x, y, lpszCaption, cObjects, lplpUnk, cPages, lpPageClsID, lcid, dwReserved, lpvReserved);
|
|||
|
}
|
|||
|
__except(Eval_Exception(GetExceptionCode()))
|
|||
|
{
|
|||
|
// No code; this block never executed.
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void ShowPPage(CString DisplayName, HWND hParentWnd)
|
|||
|
{
|
|||
|
CComPtr<IBindCtx> pBindCtx;
|
|||
|
CreateBindCtx(0, &pBindCtx);
|
|||
|
|
|||
|
CComPtr<IMoniker> pMoniker;
|
|||
|
ULONG chEaten;
|
|||
|
if(S_OK != MkParseDisplayName(pBindCtx, CStringW(DisplayName), &chEaten, &pMoniker))
|
|||
|
return;
|
|||
|
|
|||
|
CComPtr<IBaseFilter> pBF;
|
|||
|
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
|
|||
|
return;
|
|||
|
|
|||
|
ShowPPage(pBF, hParentWnd);
|
|||
|
}
|
|||
|
|
|||
|
void ShowPPage(IUnknown* pUnk, HWND hParentWnd)
|
|||
|
{
|
|||
|
CComQIPtr<ISpecifyPropertyPages> pSPP = pUnk;
|
|||
|
if(!pSPP) return;
|
|||
|
|
|||
|
CString str;
|
|||
|
|
|||
|
CComQIPtr<IBaseFilter> pBF = pSPP;
|
|||
|
CFilterInfo fi;
|
|||
|
CComQIPtr<IPin> pPin = pSPP;
|
|||
|
CPinInfo pi;
|
|||
|
if(pBF && SUCCEEDED(pBF->QueryFilterInfo(&fi)))
|
|||
|
str = fi.achName;
|
|||
|
else if(pPin && SUCCEEDED(pPin->QueryPinInfo(&pi)))
|
|||
|
str = pi.achName;
|
|||
|
|
|||
|
CAUUID caGUID;
|
|||
|
caGUID.pElems = NULL;
|
|||
|
if(SUCCEEDED(pSPP->GetPages(&caGUID)))
|
|||
|
{
|
|||
|
IUnknown* lpUnk = NULL;
|
|||
|
pSPP.QueryInterface(&lpUnk);
|
|||
|
MyOleCreatePropertyFrame(
|
|||
|
hParentWnd, 0, 0, CStringW(str),
|
|||
|
1, (IUnknown**)&lpUnk,
|
|||
|
caGUID.cElems, caGUID.pElems,
|
|||
|
0, 0, NULL);
|
|||
|
lpUnk->Release();
|
|||
|
|
|||
|
if(caGUID.pElems) CoTaskMemFree(caGUID.pElems);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CLSID GetCLSID(IBaseFilter* pBF)
|
|||
|
{
|
|||
|
CLSID clsid = GUID_NULL;
|
|||
|
if(pBF) pBF->GetClassID(&clsid);
|
|||
|
return(clsid);
|
|||
|
}
|
|||
|
|
|||
|
CLSID GetCLSID(IPin* pPin)
|
|||
|
{
|
|||
|
return(GetCLSID(GetFilterFromPin(pPin)));
|
|||
|
}
|
|||
|
|
|||
|
bool IsCLSIDRegistered(LPCTSTR clsid)
|
|||
|
{
|
|||
|
CString rootkey1(_T("CLSID\\"));
|
|||
|
CString rootkey2(_T("CLSID\\{083863F1-70DE-11d0-BD40-00A0C911CE86}\\Instance\\"));
|
|||
|
|
|||
|
return ERROR_SUCCESS == CRegKey().Open(HKEY_CLASSES_ROOT, rootkey1 + clsid, KEY_READ)
|
|||
|
|| ERROR_SUCCESS == CRegKey().Open(HKEY_CLASSES_ROOT, rootkey2 + clsid, KEY_READ);
|
|||
|
}
|
|||
|
|
|||
|
bool IsCLSIDRegistered(const CLSID& clsid)
|
|||
|
{
|
|||
|
bool fRet = false;
|
|||
|
|
|||
|
LPOLESTR pStr = NULL;
|
|||
|
if(S_OK == StringFromCLSID(clsid, &pStr) && pStr)
|
|||
|
{
|
|||
|
fRet = IsCLSIDRegistered(CString(pStr));
|
|||
|
CoTaskMemFree(pStr);
|
|||
|
}
|
|||
|
|
|||
|
return(fRet);
|
|||
|
}
|
|||
|
|
|||
|
void CStringToBin(CString str, CAtlArray<BYTE>& data)
|
|||
|
{
|
|||
|
str.Trim();
|
|||
|
ASSERT((str.GetLength()&1) == 0);
|
|||
|
data.SetCount(str.GetLength()/2);
|
|||
|
|
|||
|
BYTE b = 0;
|
|||
|
|
|||
|
str.MakeUpper();
|
|||
|
for(int i = 0, j = str.GetLength(); i < j; i++)
|
|||
|
{
|
|||
|
TCHAR c = str[i];
|
|||
|
if(c >= '0' && c <= '9')
|
|||
|
{
|
|||
|
if(!(i&1)) b = ((char(c-'0')<<4)&0xf0)|(b&0x0f);
|
|||
|
else b = (char(c-'0')&0x0f)|(b&0xf0);
|
|||
|
}
|
|||
|
else if(c >= 'A' && c <= 'F')
|
|||
|
{
|
|||
|
if(!(i&1)) b = ((char(c-'A'+10)<<4)&0xf0)|(b&0x0f);
|
|||
|
else b = (char(c-'A'+10)&0x0f)|(b&0xf0);
|
|||
|
}
|
|||
|
else break;
|
|||
|
|
|||
|
if(i&1)
|
|||
|
{
|
|||
|
data[i>>1] = b;
|
|||
|
b = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CString BinToCString(BYTE* ptr, int len)
|
|||
|
{
|
|||
|
CString ret;
|
|||
|
|
|||
|
while(len-- > 0)
|
|||
|
{
|
|||
|
TCHAR high, low;
|
|||
|
high = (*ptr>>4) >= 10 ? (*ptr>>4)-10 + 'A' : (*ptr>>4) + '0';
|
|||
|
low = (*ptr&0xf) >= 10 ? (*ptr&0xf)-10 + 'A' : (*ptr&0xf) + '0';
|
|||
|
|
|||
|
CString str;
|
|||
|
str.Format(_T("%c%c"), high, low);
|
|||
|
ret += str;
|
|||
|
|
|||
|
ptr++;
|
|||
|
}
|
|||
|
|
|||
|
return(ret);
|
|||
|
}
|
|||
|
|
|||
|
static void FindFiles(CString fn, CAtlList<CString>& files)
|
|||
|
{
|
|||
|
CString path = fn;
|
|||
|
path.Replace('/', '\\');
|
|||
|
path = path.Left(path.ReverseFind('\\')+1);
|
|||
|
|
|||
|
WIN32_FIND_DATA findData;
|
|||
|
HANDLE h = FindFirstFile(fn, &findData);
|
|||
|
if(h != INVALID_HANDLE_VALUE)
|
|||
|
{
|
|||
|
do {files.AddTail(path + findData.cFileName);}
|
|||
|
while(FindNextFile(h, &findData));
|
|||
|
|
|||
|
FindClose(h);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cdrom_t GetCDROMType(TCHAR drive, CAtlList<CString>& files)
|
|||
|
{
|
|||
|
files.RemoveAll();
|
|||
|
|
|||
|
CString path;
|
|||
|
path.Format(_T("%c:"), drive);
|
|||
|
|
|||
|
if(GetDriveType(path + _T("\\")) == DRIVE_CDROM)
|
|||
|
{
|
|||
|
// CDROM_VideoCD
|
|||
|
FindFiles(path + _T("\\mpegav\\avseq??.dat"), files);
|
|||
|
FindFiles(path + _T("\\mpegav\\avseq??.mpg"), files);
|
|||
|
FindFiles(path + _T("\\mpeg2\\avseq??.dat"), files);
|
|||
|
FindFiles(path + _T("\\mpeg2\\avseq??.mpg"), files);
|
|||
|
FindFiles(path + _T("\\mpegav\\music??.dat"), files);
|
|||
|
FindFiles(path + _T("\\mpegav\\music??.mpg"), files);
|
|||
|
FindFiles(path + _T("\\mpeg2\\music??.dat"), files);
|
|||
|
FindFiles(path + _T("\\mpeg2\\music??.mpg"), files);
|
|||
|
if(files.GetCount() > 0) return CDROM_VideoCD;
|
|||
|
|
|||
|
// CDROM_DVDVideo
|
|||
|
FindFiles(path + _T("\\VIDEO_TS\\video_ts.ifo"), files);
|
|||
|
if(files.GetCount() > 0) return CDROM_DVDVideo;
|
|||
|
|
|||
|
// CDROM_Audio
|
|||
|
if(!(GetVersion()&0x80000000))
|
|||
|
{
|
|||
|
HANDLE hDrive = CreateFile(CString(_T("\\\\.\\")) + path, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
|
|||
|
if(hDrive != INVALID_HANDLE_VALUE)
|
|||
|
{
|
|||
|
DWORD BytesReturned;
|
|||
|
CDROM_TOC TOC;
|
|||
|
if(DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC, NULL, 0, &TOC, sizeof(TOC), &BytesReturned, 0))
|
|||
|
{
|
|||
|
for(int i = TOC.FirstTrack; i <= TOC.LastTrack; i++)
|
|||
|
{
|
|||
|
// MMC-3 Draft Revision 10g: Table 222 <20> Q Sub-channel control field
|
|||
|
TOC.TrackData[i-1].Control &= 5;
|
|||
|
if(TOC.TrackData[i-1].Control == 0 || TOC.TrackData[i-1].Control == 1)
|
|||
|
{
|
|||
|
CString fn;
|
|||
|
fn.Format(_T("%s\\track%02d.cda"), path, i);
|
|||
|
files.AddTail(fn);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CloseHandle(hDrive);
|
|||
|
}
|
|||
|
}
|
|||
|
if(files.GetCount() > 0) return CDROM_Audio;
|
|||
|
|
|||
|
// it is a cdrom but nothing special
|
|||
|
return CDROM_Unknown;
|
|||
|
}
|
|||
|
|
|||
|
return CDROM_NotFound;
|
|||
|
}
|
|||
|
|
|||
|
CString GetDriveLabel(TCHAR drive)
|
|||
|
{
|
|||
|
CString label;
|
|||
|
|
|||
|
CString path;
|
|||
|
path.Format(_T("%c:\\"), drive);
|
|||
|
TCHAR VolumeNameBuffer[MAX_PATH], FileSystemNameBuffer[MAX_PATH];
|
|||
|
DWORD VolumeSerialNumber, MaximumComponentLength, FileSystemFlags;
|
|||
|
if(GetVolumeInformation(path,
|
|||
|
VolumeNameBuffer, MAX_PATH, &VolumeSerialNumber, &MaximumComponentLength,
|
|||
|
&FileSystemFlags, FileSystemNameBuffer, MAX_PATH))
|
|||
|
{
|
|||
|
label = VolumeNameBuffer;
|
|||
|
}
|
|||
|
|
|||
|
return(label);
|
|||
|
}
|
|||
|
|
|||
|
bool GetKeyFrames(CString fn, CUIntArray& kfs)
|
|||
|
{
|
|||
|
kfs.RemoveAll();
|
|||
|
|
|||
|
CString fn2 = CString(fn).MakeLower();
|
|||
|
if(fn2.Mid(fn2.ReverseFind('.')+1) == _T("avi"))
|
|||
|
{
|
|||
|
AVIFileInit();
|
|||
|
|
|||
|
PAVIFILE pfile;
|
|||
|
if(AVIFileOpen(&pfile, fn, OF_SHARE_DENY_WRITE, 0L) == 0)
|
|||
|
{
|
|||
|
AVIFILEINFO afi;
|
|||
|
memset(&afi, 0, sizeof(afi));
|
|||
|
AVIFileInfo(pfile, &afi, sizeof(AVIFILEINFO));
|
|||
|
|
|||
|
CComPtr<IAVIStream> pavi;
|
|||
|
if(AVIFileGetStream(pfile, &pavi, streamtypeVIDEO, 0) == AVIERR_OK)
|
|||
|
{
|
|||
|
AVISTREAMINFO si;
|
|||
|
AVIStreamInfo(pavi, &si, sizeof(si));
|
|||
|
|
|||
|
if(afi.dwCaps&AVIFILECAPS_ALLKEYFRAMES)
|
|||
|
{
|
|||
|
kfs.SetSize(si.dwLength);
|
|||
|
for(int kf = 0; kf < si.dwLength; kf++) kfs[kf] = kf;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
for(int kf = 0; ; kf++)
|
|||
|
{
|
|||
|
kf = pavi->FindSample(kf, FIND_KEY|FIND_NEXT);
|
|||
|
if(kf < 0 || kfs.GetCount() > 0 && kfs[kfs.GetCount()-1] >= kf) break;
|
|||
|
kfs.Add(kf);
|
|||
|
}
|
|||
|
|
|||
|
if(kfs.GetCount() > 0 && kfs[kfs.GetCount()-1] < si.dwLength-1)
|
|||
|
{
|
|||
|
kfs.Add(si.dwLength-1);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
AVIFileRelease(pfile);
|
|||
|
}
|
|||
|
|
|||
|
AVIFileExit();
|
|||
|
}
|
|||
|
|
|||
|
return(kfs.GetCount() > 0);
|
|||
|
}
|
|||
|
|
|||
|
DVD_HMSF_TIMECODE RT2HMSF(REFERENCE_TIME rt, double fps)
|
|||
|
{
|
|||
|
DVD_HMSF_TIMECODE hmsf =
|
|||
|
{
|
|||
|
(BYTE)((rt/10000000/60/60)),
|
|||
|
(BYTE)((rt/10000000/60)%60),
|
|||
|
(BYTE)((rt/10000000)%60),
|
|||
|
(BYTE)(1.0*((rt/10000)%1000) * fps / 1000)
|
|||
|
};
|
|||
|
|
|||
|
return hmsf;
|
|||
|
}
|
|||
|
|
|||
|
REFERENCE_TIME HMSF2RT(DVD_HMSF_TIMECODE hmsf, double fps)
|
|||
|
{
|
|||
|
if(fps == 0) {hmsf.bFrames = 0; fps = 1;}
|
|||
|
return (REFERENCE_TIME)((((REFERENCE_TIME)hmsf.bHours*60+hmsf.bMinutes)*60+hmsf.bSeconds)*1000+1.0*hmsf.bFrames*1000/fps)*10000;
|
|||
|
}
|
|||
|
|
|||
|
void memsetd(void* dst, unsigned int c, int nbytes)
|
|||
|
{
|
|||
|
__asm
|
|||
|
{
|
|||
|
mov eax, c
|
|||
|
mov ecx, nbytes
|
|||
|
shr ecx, 2
|
|||
|
mov edi, dst
|
|||
|
cld
|
|||
|
rep stosd
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool ExtractBIH(const AM_MEDIA_TYPE* pmt, BITMAPINFOHEADER* bih)
|
|||
|
{
|
|||
|
if(pmt && bih)
|
|||
|
{
|
|||
|
memset(bih, 0, sizeof(*bih));
|
|||
|
|
|||
|
if(pmt->formattype == FORMAT_VideoInfo)
|
|||
|
{
|
|||
|
VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->pbFormat;
|
|||
|
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
|
|||
|
return true;
|
|||
|
}
|
|||
|
else if(pmt->formattype == FORMAT_VideoInfo2)
|
|||
|
{
|
|||
|
VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->pbFormat;
|
|||
|
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
|
|||
|
return true;
|
|||
|
}
|
|||
|
else if(pmt->formattype == FORMAT_MPEGVideo)
|
|||
|
{
|
|||
|
VIDEOINFOHEADER* vih = &((MPEG1VIDEOINFO*)pmt->pbFormat)->hdr;
|
|||
|
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
|
|||
|
return true;
|
|||
|
}
|
|||
|
else if(pmt->formattype == FORMAT_MPEG2_VIDEO)
|
|||
|
{
|
|||
|
VIDEOINFOHEADER2* vih = &((MPEG2VIDEOINFO*)pmt->pbFormat)->hdr;
|
|||
|
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
|
|||
|
return true;
|
|||
|
}
|
|||
|
else if(pmt->formattype == FORMAT_DiracVideoInfo)
|
|||
|
{
|
|||
|
VIDEOINFOHEADER2* vih = &((DIRACINFOHEADER*)pmt->pbFormat)->hdr;
|
|||
|
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
bool ExtractBIH(IMediaSample* pMS, BITMAPINFOHEADER* bih)
|
|||
|
{
|
|||
|
AM_MEDIA_TYPE* pmt = NULL;
|
|||
|
pMS->GetMediaType(&pmt);
|
|||
|
if(pmt)
|
|||
|
{
|
|||
|
bool fRet = ExtractBIH(pmt, bih);
|
|||
|
DeleteMediaType(pmt);
|
|||
|
return(fRet);
|
|||
|
}
|
|||
|
|
|||
|
return(false);
|
|||
|
}
|
|||
|
|
|||
|
bool ExtractDim(const AM_MEDIA_TYPE* pmt, int& w, int& h, int& arx, int& ary)
|
|||
|
{
|
|||
|
w = h = arx = ary = 0;
|
|||
|
|
|||
|
if(pmt->formattype == FORMAT_VideoInfo || pmt->formattype == FORMAT_MPEGVideo)
|
|||
|
{
|
|||
|
VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->pbFormat;
|
|||
|
w = vih->bmiHeader.biWidth;
|
|||
|
h = abs(vih->bmiHeader.biHeight);
|
|||
|
arx = w * vih->bmiHeader.biYPelsPerMeter;
|
|||
|
ary = h * vih->bmiHeader.biXPelsPerMeter;
|
|||
|
}
|
|||
|
else if(pmt->formattype == FORMAT_VideoInfo2 || pmt->formattype == FORMAT_MPEG2_VIDEO || pmt->formattype == FORMAT_DiracVideoInfo)
|
|||
|
{
|
|||
|
VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->pbFormat;
|
|||
|
w = vih->bmiHeader.biWidth;
|
|||
|
h = abs(vih->bmiHeader.biHeight);
|
|||
|
arx = vih->dwPictAspectRatioX;
|
|||
|
ary = vih->dwPictAspectRatioY;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return(false);
|
|||
|
}
|
|||
|
|
|||
|
if(!arx || !ary)
|
|||
|
{
|
|||
|
arx = w;
|
|||
|
ary = h;
|
|||
|
}
|
|||
|
|
|||
|
BYTE* ptr = NULL;
|
|||
|
DWORD len = 0;
|
|||
|
|
|||
|
if(pmt->formattype == FORMAT_MPEGVideo)
|
|||
|
{
|
|||
|
ptr = ((MPEG1VIDEOINFO*)pmt->pbFormat)->bSequenceHeader;
|
|||
|
len = ((MPEG1VIDEOINFO*)pmt->pbFormat)->cbSequenceHeader;
|
|||
|
|
|||
|
if(ptr && len >= 8 && *(DWORD*)ptr == 0xb3010000)
|
|||
|
{
|
|||
|
w = (ptr[4]<<4)|(ptr[5]>>4);
|
|||
|
h = ((ptr[5]&0xf)<<8)|ptr[6];
|
|||
|
float ar[] =
|
|||
|
{
|
|||
|
1.0000f,1.0000f,0.6735f,0.7031f,
|
|||
|
0.7615f,0.8055f,0.8437f,0.8935f,
|
|||
|
0.9157f,0.9815f,1.0255f,1.0695f,
|
|||
|
1.0950f,1.1575f,1.2015f,1.0000f,
|
|||
|
};
|
|||
|
arx = (int)((float)w / ar[ptr[7]>>4] + 0.5);
|
|||
|
ary = h;
|
|||
|
}
|
|||
|
}
|
|||
|
else if(pmt->formattype == FORMAT_MPEG2_VIDEO)
|
|||
|
{
|
|||
|
ptr = (BYTE*)((MPEG2VIDEOINFO*)pmt->pbFormat)->dwSequenceHeader;
|
|||
|
len = ((MPEG2VIDEOINFO*)pmt->pbFormat)->cbSequenceHeader;
|
|||
|
|
|||
|
if(ptr && len >= 8 && *(DWORD*)ptr == 0xb3010000)
|
|||
|
{
|
|||
|
w = (ptr[4]<<4)|(ptr[5]>>4);
|
|||
|
h = ((ptr[5]&0xf)<<8)|ptr[6];
|
|||
|
struct {int x, y;} ar[] = {{w,h},{4,3},{16,9},{221,100},{w,h}};
|
|||
|
int i = min(max(ptr[7]>>4, 1), 5)-1;
|
|||
|
arx = ar[i].x;
|
|||
|
ary = ar[i].y;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(ptr && len >= 8)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DWORD a = arx, b = ary;
|
|||
|
while(a) {int tmp = a; a = b % tmp; b = tmp;}
|
|||
|
if(b) arx /= b, ary /= b;
|
|||
|
|
|||
|
return(true);
|
|||
|
}
|
|||
|
|
|||
|
bool MakeMPEG2MediaType(CMediaType& mt, BYTE* seqhdr, DWORD len, int w, int h)
|
|||
|
{
|
|||
|
if(len < 4 || *(DWORD*)seqhdr != 0xb3010000) return false;
|
|||
|
|
|||
|
BYTE* seqhdr_ext = NULL;
|
|||
|
|
|||
|
BYTE* seqhdr_end = seqhdr + 11;
|
|||
|
if(seqhdr_end - seqhdr > len) return false;
|
|||
|
if(*seqhdr_end & 0x02) seqhdr_end += 64;
|
|||
|
if(seqhdr_end - seqhdr > len) return false;
|
|||
|
if(*seqhdr_end & 0x01) seqhdr_end += 64;
|
|||
|
if(seqhdr_end - seqhdr > len) return false;
|
|||
|
seqhdr_end++;
|
|||
|
if(seqhdr_end - seqhdr > len) return false;
|
|||
|
if(len - (seqhdr_end - seqhdr) > 4 && *(DWORD*)seqhdr_end == 0xb5010000) {seqhdr_ext = seqhdr_end; seqhdr_end += 10;}
|
|||
|
if(seqhdr_end - seqhdr > len) return false;
|
|||
|
|
|||
|
len = seqhdr_end - seqhdr;
|
|||
|
|
|||
|
mt = CMediaType();
|
|||
|
|
|||
|
mt.majortype = MEDIATYPE_Video;
|
|||
|
mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO;
|
|||
|
mt.formattype = FORMAT_MPEG2Video;
|
|||
|
|
|||
|
MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + len);
|
|||
|
memset(mt.Format(), 0, mt.FormatLength());
|
|||
|
vih->hdr.bmiHeader.biSize = sizeof(vih->hdr.bmiHeader);
|
|||
|
vih->hdr.bmiHeader.biWidth = w;
|
|||
|
vih->hdr.bmiHeader.biHeight = h;
|
|||
|
|
|||
|
BYTE* pSequenceHeader = (BYTE*)vih->dwSequenceHeader;
|
|||
|
memcpy(pSequenceHeader, seqhdr, len);
|
|||
|
vih->cbSequenceHeader = len;
|
|||
|
|
|||
|
static char profile[8] =
|
|||
|
{
|
|||
|
0, AM_MPEG2Profile_High, AM_MPEG2Profile_SpatiallyScalable, AM_MPEG2Profile_SNRScalable,
|
|||
|
AM_MPEG2Profile_Main, AM_MPEG2Profile_Simple, 0, 0
|
|||
|
};
|
|||
|
|
|||
|
static char level[16] =
|
|||
|
{
|
|||
|
0, 0, 0, 0,
|
|||
|
AM_MPEG2Level_High, 0, AM_MPEG2Level_High1440, 0,
|
|||
|
AM_MPEG2Level_Main, 0, AM_MPEG2Level_Low, 0,
|
|||
|
0, 0, 0, 0
|
|||
|
};
|
|||
|
|
|||
|
if(seqhdr_ext && (seqhdr_ext[4] & 0xf0) == 0x10)
|
|||
|
{
|
|||
|
vih->dwProfile = profile[seqhdr_ext[4] & 0x07];
|
|||
|
vih->dwLevel = level[seqhdr_ext[5] >> 4];
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
unsigned __int64 GetFileVersion(LPCTSTR fn)
|
|||
|
{
|
|||
|
unsigned __int64 ret = 0;
|
|||
|
|
|||
|
DWORD buff[4];
|
|||
|
VS_FIXEDFILEINFO* pvsf = (VS_FIXEDFILEINFO*)buff;
|
|||
|
DWORD d; // a variable that GetFileVersionInfoSize sets to zero (but why is it needed ?????????????????????????????? :)
|
|||
|
DWORD len = GetFileVersionInfoSize((TCHAR*)fn, &d);
|
|||
|
|
|||
|
if(len)
|
|||
|
{
|
|||
|
TCHAR* b1 = new TCHAR[len];
|
|||
|
if(b1)
|
|||
|
{
|
|||
|
UINT uLen;
|
|||
|
if(GetFileVersionInfo((TCHAR*)fn, 0, len, b1) && VerQueryValue(b1, _T("\\"), (void**)&pvsf, &uLen))
|
|||
|
{
|
|||
|
ret = ((unsigned __int64)pvsf->dwFileVersionMS<<32) | pvsf->dwFileVersionLS;
|
|||
|
}
|
|||
|
|
|||
|
delete [] b1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
bool CreateFilter(CStringW DisplayName, IBaseFilter** ppBF, CStringW& FriendlyName)
|
|||
|
{
|
|||
|
if(!ppBF) return(false);
|
|||
|
|
|||
|
*ppBF = NULL;
|
|||
|
FriendlyName.Empty();
|
|||
|
|
|||
|
CComPtr<IBindCtx> pBindCtx;
|
|||
|
CreateBindCtx(0, &pBindCtx);
|
|||
|
|
|||
|
CComPtr<IMoniker> pMoniker;
|
|||
|
ULONG chEaten;
|
|||
|
if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
|
|||
|
return(false);
|
|||
|
|
|||
|
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)ppBF)) || !*ppBF)
|
|||
|
return(false);
|
|||
|
|
|||
|
CComPtr<IPropertyBag> pPB;
|
|||
|
CComVariant var;
|
|||
|
if(SUCCEEDED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB))
|
|||
|
&& SUCCEEDED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
|
|||
|
FriendlyName = var.bstrVal;
|
|||
|
|
|||
|
return(true);
|
|||
|
}
|
|||
|
|
|||
|
IBaseFilter* AppendFilter(IPin* pPin, IMoniker* pMoniker, IGraphBuilder* pGB)
|
|||
|
{
|
|||
|
do
|
|||
|
{
|
|||
|
if(!pPin || !pMoniker || !pGB)
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IPin> pPinTo;
|
|||
|
PIN_DIRECTION dir;
|
|||
|
if(FAILED(pPin->QueryDirection(&dir)) || dir != PINDIR_OUTPUT || SUCCEEDED(pPin->ConnectedTo(&pPinTo)))
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IBindCtx> pBindCtx;
|
|||
|
CreateBindCtx(0, &pBindCtx);
|
|||
|
|
|||
|
CComPtr<IPropertyBag> pPB;
|
|||
|
if(FAILED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB)))
|
|||
|
break;
|
|||
|
|
|||
|
CComVariant var;
|
|||
|
if(FAILED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
|
|||
|
break;
|
|||
|
|
|||
|
CComPtr<IBaseFilter> pBF;
|
|||
|
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
|
|||
|
break;
|
|||
|
|
|||
|
if(FAILED(pGB->AddFilter(pBF, CStringW(var.bstrVal))))
|
|||
|
break;
|
|||
|
|
|||
|
BeginEnumPins(pBF, pEP, pPinTo)
|
|||
|
{
|
|||
|
PIN_DIRECTION dir;
|
|||
|
if(FAILED(pPinTo->QueryDirection(&dir)) || dir != PINDIR_INPUT)
|
|||
|
continue;
|
|||
|
|
|||
|
if(SUCCEEDED(pGB->ConnectDirect(pPin, pPinTo, NULL)))
|
|||
|
return(pBF);
|
|||
|
}
|
|||
|
EndEnumFilters
|
|||
|
|
|||
|
pGB->RemoveFilter(pBF);
|
|||
|
}
|
|||
|
while(false);
|
|||
|
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
|
|||
|
CStringW GetFriendlyName(CStringW DisplayName)
|
|||
|
{
|
|||
|
CStringW FriendlyName;
|
|||
|
|
|||
|
CComPtr<IBindCtx> pBindCtx;
|
|||
|
CreateBindCtx(0, &pBindCtx);
|
|||
|
|
|||
|
CComPtr<IMoniker> pMoniker;
|
|||
|
ULONG chEaten;
|
|||
|
if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
|
|||
|
return(false);
|
|||
|
|
|||
|
CComPtr<IPropertyBag> pPB;
|
|||
|
CComVariant var;
|
|||
|
if(SUCCEEDED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB))
|
|||
|
&& SUCCEEDED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
|
|||
|
FriendlyName = var.bstrVal;
|
|||
|
|
|||
|
return FriendlyName;
|
|||
|
}
|
|||
|
|
|||
|
typedef struct
|
|||
|
{
|
|||
|
CString path;
|
|||
|
HINSTANCE hInst;
|
|||
|
CLSID clsid;
|
|||
|
} ExternalObject;
|
|||
|
|
|||
|
static CAtlList<ExternalObject> s_extobjs;
|
|||
|
|
|||
|
HRESULT LoadExternalObject(LPCTSTR path, REFCLSID clsid, REFIID iid, void** ppv)
|
|||
|
{
|
|||
|
CheckPointer(ppv, E_POINTER);
|
|||
|
|
|||
|
CString fullpath = MakeFullPath(path);
|
|||
|
|
|||
|
HINSTANCE hInst = NULL;
|
|||
|
bool fFound = false;
|
|||
|
|
|||
|
POSITION pos = s_extobjs.GetHeadPosition();
|
|||
|
while(pos)
|
|||
|
{
|
|||
|
ExternalObject& eo = s_extobjs.GetNext(pos);
|
|||
|
if(!eo.path.CompareNoCase(fullpath))
|
|||
|
{
|
|||
|
hInst = eo.hInst;
|
|||
|
fFound = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
HRESULT hr = E_FAIL;
|
|||
|
|
|||
|
if(hInst || (hInst = CoLoadLibrary(CComBSTR(fullpath), TRUE)))
|
|||
|
{
|
|||
|
typedef HRESULT (__stdcall * PDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
|
|||
|
PDllGetClassObject p = (PDllGetClassObject)GetProcAddress(hInst, "DllGetClassObject");
|
|||
|
|
|||
|
if(p && FAILED(hr = p(clsid, iid, ppv)))
|
|||
|
{
|
|||
|
CComPtr<IClassFactory> pCF;
|
|||
|
if(SUCCEEDED(hr = p(clsid, __uuidof(IClassFactory), (void**)&pCF)))
|
|||
|
{
|
|||
|
hr = pCF->CreateInstance(NULL, iid, ppv);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(FAILED(hr) && hInst && !fFound)
|
|||
|
{
|
|||
|
CoFreeLibrary(hInst);
|
|||
|
return hr;
|
|||
|
}
|
|||
|
|
|||
|
if(hInst && !fFound)
|
|||
|
{
|
|||
|
ExternalObject eo;
|
|||
|
eo.path = fullpath;
|
|||
|
eo.hInst = hInst;
|
|||
|
eo.clsid = clsid;
|
|||
|
s_extobjs.AddTail(eo);
|
|||
|
}
|
|||
|
|
|||
|
return hr;
|
|||
|
}
|
|||
|
|
|||
|
HRESULT LoadExternalFilter(LPCTSTR path, REFCLSID clsid, IBaseFilter** ppBF)
|
|||
|
{
|
|||
|
return LoadExternalObject(path, clsid, __uuidof(IBaseFilter), (void**)ppBF);
|
|||
|
}
|
|||
|
|
|||
|
HRESULT LoadExternalPropertyPage(IPersist* pP, REFCLSID clsid, IPropertyPage** ppPP)
|
|||
|
{
|
|||
|
CLSID clsid2 = GUID_NULL;
|
|||
|
if(FAILED(pP->GetClassID(&clsid2))) return E_FAIL;
|
|||
|
|
|||
|
POSITION pos = s_extobjs.GetHeadPosition();
|
|||
|
while(pos)
|
|||
|
{
|
|||
|
ExternalObject& eo = s_extobjs.GetNext(pos);
|
|||
|
if(eo.clsid == clsid2)
|
|||
|
{
|
|||
|
return LoadExternalObject(eo.path, clsid, __uuidof(IPropertyPage), (void**)ppPP);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return E_FAIL;
|
|||
|
}
|
|||
|
|
|||
|
void UnloadExternalObjects()
|
|||
|
{
|
|||
|
POSITION pos = s_extobjs.GetHeadPosition();
|
|||
|
while(pos)
|
|||
|
{
|
|||
|
ExternalObject& eo = s_extobjs.GetNext(pos);
|
|||
|
CoFreeLibrary(eo.hInst);
|
|||
|
}
|
|||
|
s_extobjs.RemoveAll();
|
|||
|
}
|
|||
|
|
|||
|
CString MakeFullPath(LPCTSTR path)
|
|||
|
{
|
|||
|
CString full(path);
|
|||
|
full.Replace('/', '\\');
|
|||
|
|
|||
|
CString fn;
|
|||
|
fn.ReleaseBuffer(GetModuleFileName(AfxGetInstanceHandle(), fn.GetBuffer(MAX_PATH), MAX_PATH));
|
|||
|
CPath p(fn);
|
|||
|
|
|||
|
if(full.GetLength() >= 2 && full[0] == '\\' && full[1] != '\\')
|
|||
|
{
|
|||
|
p.StripToRoot();
|
|||
|
full = CString(p) + full.Mid(1);
|
|||
|
}
|
|||
|
else if(full.Find(_T(":\\")) < 0)
|
|||
|
{
|
|||
|
p.RemoveFileSpec();
|
|||
|
p.AddBackslash();
|
|||
|
full = CString(p) + full;
|
|||
|
}
|
|||
|
|
|||
|
CPath c(full);
|
|||
|
c.Canonicalize();
|
|||
|
return CString(c);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
|
|||
|
CString GetMediaTypeName(const GUID& guid)
|
|||
|
{
|
|||
|
CString ret = guid == GUID_NULL
|
|||
|
? _T("Any type")
|
|||
|
: CString(GuidNames[guid]);
|
|||
|
|
|||
|
if(ret == _T("FOURCC GUID"))
|
|||
|
{
|
|||
|
CString str;
|
|||
|
if(guid.Data1 >= 0x10000)
|
|||
|
str.Format(_T("Video: %c%c%c%c"), (guid.Data1>>0)&0xff, (guid.Data1>>8)&0xff, (guid.Data1>>16)&0xff, (guid.Data1>>24)&0xff);
|
|||
|
else
|
|||
|
str.Format(_T("Audio: 0x%08x"), guid.Data1);
|
|||
|
ret = str;
|
|||
|
}
|
|||
|
else if(ret == _T("Unknown GUID Name"))
|
|||
|
{
|
|||
|
WCHAR null[128] = {0}, buff[128];
|
|||
|
StringFromGUID2(GUID_NULL, null, 127);
|
|||
|
ret = CString(CStringW(StringFromGUID2(guid, buff, 127) ? buff : null));
|
|||
|
}
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
GUID GUIDFromCString(CString str)
|
|||
|
{
|
|||
|
GUID guid = GUID_NULL;
|
|||
|
HRESULT hr = CLSIDFromString(CComBSTR(str), &guid);
|
|||
|
ASSERT(SUCCEEDED(hr));
|
|||
|
return guid;
|
|||
|
}
|
|||
|
|
|||
|
HRESULT GUIDFromCString(CString str, GUID& guid)
|
|||
|
{
|
|||
|
guid = GUID_NULL;
|
|||
|
return CLSIDFromString(CComBSTR(str), &guid);
|
|||
|
}
|
|||
|
|
|||
|
CString CStringFromGUID(const GUID& guid)
|
|||
|
{
|
|||
|
WCHAR null[128] = {0}, buff[128];
|
|||
|
StringFromGUID2(GUID_NULL, null, 127);
|
|||
|
return CString(StringFromGUID2(guid, buff, 127) > 0 ? buff : null);
|
|||
|
}
|
|||
|
|
|||
|
CStringW UTF8To16(LPCSTR utf8)
|
|||
|
{
|
|||
|
CStringW str;
|
|||
|
int n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)-1;
|
|||
|
if(n < 0) return str;
|
|||
|
str.ReleaseBuffer(MultiByteToWideChar(CP_UTF8, 0, utf8, -1, str.GetBuffer(n), n+1)-1);
|
|||
|
return str;
|
|||
|
}
|
|||
|
|
|||
|
CStringA UTF16To8(LPCWSTR utf16)
|
|||
|
{
|
|||
|
CStringA str;
|
|||
|
int n = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, 0, NULL, NULL)-1;
|
|||
|
if(n < 0) return str;
|
|||
|
str.ReleaseBuffer(WideCharToMultiByte(CP_UTF8, 0, utf16, -1, str.GetBuffer(n), n+1, NULL, NULL)-1);
|
|||
|
return str;
|
|||
|
}
|
|||
|
|
|||
|
static struct {LPCSTR name, iso6392, iso6391;} s_isolangs[] =
|
|||
|
{
|
|||
|
{"Abkhazian", "abk", "ab"},
|
|||
|
{"Achinese", "ace", ""},
|
|||
|
{"Acoli", "ach", ""},
|
|||
|
{"Adangme", "ada", ""},
|
|||
|
{"Afar", "aar", "aa"},
|
|||
|
{"Afrihili", "afh", ""},
|
|||
|
{"Afrikaans", "afr", "af"},
|
|||
|
{"Afro-Asiatic (Other)", "afa", ""},
|
|||
|
{"Akan", "aka", "ak"},
|
|||
|
{"Akkadian", "akk", ""},
|
|||
|
{"Albanian", "alb", "sq"},
|
|||
|
{"Albanian", "sqi", "sq"},
|
|||
|
{"Aleut", "ale", ""},
|
|||
|
{"Algonquian languages", "alg", ""},
|
|||
|
{"Altaic (Other)", "tut", ""},
|
|||
|
{"Amharic", "amh", "am"},
|
|||
|
{"Apache languages", "apa", ""},
|
|||
|
{"Arabic", "ara", "ar"},
|
|||
|
{"Aragonese", "arg", "an"},
|
|||
|
{"Aramaic", "arc", ""},
|
|||
|
{"Arapaho", "arp", ""},
|
|||
|
{"Araucanian", "arn", ""},
|
|||
|
{"Arawak", "arw", ""},
|
|||
|
{"Armenian", "arm", "hy"},
|
|||
|
{"Armenian", "hye", "hy"},
|
|||
|
{"Artificial (Other)", "art", ""},
|
|||
|
{"Assamese", "asm", "as"},
|
|||
|
{"Asturian; Bable", "ast", ""},
|
|||
|
{"Athapascan languages", "ath", ""},
|
|||
|
{"Australian languages", "aus", ""},
|
|||
|
{"Austronesian (Other)", "map", ""},
|
|||
|
{"Avaric", "ava", "av"},
|
|||
|
{"Avestan", "ave", "ae"},
|
|||
|
{"Awadhi", "awa", ""},
|
|||
|
{"Aymara", "aym", "ay"},
|
|||
|
{"Azerbaijani", "aze", "az"},
|
|||
|
{"Bable; Asturian", "ast", ""},
|
|||
|
{"Balinese", "ban", ""},
|
|||
|
{"Baltic (Other)", "bat", ""},
|
|||
|
{"Baluchi", "bal", ""},
|
|||
|
{"Bambara", "bam", "bm"},
|
|||
|
{"Bamileke languages", "bai", ""},
|
|||
|
{"Banda", "bad", ""},
|
|||
|
{"Bantu (Other)", "bnt", ""},
|
|||
|
{"Basa", "bas", ""},
|
|||
|
{"Bashkir", "bak", "ba"},
|
|||
|
{"Basque", "baq", "eu"},
|
|||
|
{"Basque", "eus", "eu"},
|
|||
|
{"Batak (Indonesia)", "btk", ""},
|
|||
|
{"Beja", "bej", ""},
|
|||
|
{"Belarusian", "bel", "be"},
|
|||
|
{"Bemba", "bem", ""},
|
|||
|
{"Bengali", "ben", "bn"},
|
|||
|
{"Berber (Other)", "ber", ""},
|
|||
|
{"Bhojpuri", "bho", ""},
|
|||
|
{"Bihari", "bih", "bh"},
|
|||
|
{"Bikol", "bik", ""},
|
|||
|
{"Bini", "bin", ""},
|
|||
|
{"Bislama", "bis", "bi"},
|
|||
|
{"Bokm<EFBFBD>l, Norwegian; Norwegian Bokm<6B>l", "nob", "nb"},
|
|||
|
{"Bosnian", "bos", "bs"},
|
|||
|
{"Braj", "bra", ""},
|
|||
|
{"Breton", "bre", "br"},
|
|||
|
{"Buginese", "bug", ""},
|
|||
|
{"Bulgarian", "bul", "bg"},
|
|||
|
{"Buriat", "bua", ""},
|
|||
|
{"Burmese", "bur", "my"},
|
|||
|
{"Burmese", "mya", "my"},
|
|||
|
{"Caddo", "cad", ""},
|
|||
|
{"Carib", "car", ""},
|
|||
|
{"Spanish; Castilian", "spa", "es"},
|
|||
|
{"Catalan", "cat", "ca"},
|
|||
|
{"Caucasian (Other)", "cau", ""},
|
|||
|
{"Cebuano", "ceb", ""},
|
|||
|
{"Celtic (Other)", "cel", ""},
|
|||
|
{"Central American Indian (Other)", "cai", ""},
|
|||
|
{"Chagatai", "chg", ""},
|
|||
|
{"Chamic languages", "cmc", ""},
|
|||
|
{"Chamorro", "cha", "ch"},
|
|||
|
{"Chechen", "che", "ce"},
|
|||
|
{"Cherokee", "chr", ""},
|
|||
|
{"Chewa; Chichewa; Nyanja", "nya", "ny"},
|
|||
|
{"Cheyenne", "chy", ""},
|
|||
|
{"Chibcha", "chb", ""},
|
|||
|
{"Chichewa; Chewa; Nyanja", "nya", "ny"},
|
|||
|
{"Chinese", "chi", "zh"},
|
|||
|
{"Chinese", "zho", "zh"},
|
|||
|
{"Chinook jargon", "chn", ""},
|
|||
|
{"Chipewyan", "chp", ""},
|
|||
|
{"Choctaw", "cho", ""},
|
|||
|
{"Chuang; Zhuang", "zha", "za"},
|
|||
|
{"Church Slavic; Old Church Slavonic", "chu", "cu"},
|
|||
|
{"Old Church Slavonic; Old Slavonic; ", "chu", "cu"},
|
|||
|
{"Church Slavonic; Old Bulgarian; Church Slavic;", "chu", "cu"},
|
|||
|
{"Old Slavonic; Church Slavonic; Old Bulgarian;", "chu", "cu"},
|
|||
|
{"Church Slavic; Old Church Slavonic", "chu", "cu"},
|
|||
|
{"Chuukese", "chk", ""},
|
|||
|
{"Chuvash", "chv", "cv"},
|
|||
|
{"Coptic", "cop", ""},
|
|||
|
{"Cornish", "cor", "kw"},
|
|||
|
{"Corsican", "cos", "co"},
|
|||
|
{"Cree", "cre", "cr"},
|
|||
|
{"Creek", "mus", ""},
|
|||
|
{"Creoles and pidgins (Other)", "crp", ""},
|
|||
|
{"Creoles and pidgins,", "cpe", ""},
|
|||
|
// {"English-based (Other)", "", ""},
|
|||
|
{"Creoles and pidgins,", "cpf", ""},
|
|||
|
// {"French-based (Other)", "", ""},
|
|||
|
{"Creoles and pidgins,", "cpp", ""},
|
|||
|
// {"Portuguese-based (Other)", "", ""},
|
|||
|
{"Croatian", "scr", "hr"},
|
|||
|
{"Croatian", "hrv", "hr"},
|
|||
|
{"Cushitic (Other)", "cus", ""},
|
|||
|
{"Czech", "cze", "cs"},
|
|||
|
{"Czech", "ces", "cs"},
|
|||
|
{"Dakota", "dak", ""},
|
|||
|
{"Danish", "dan", "da"},
|
|||
|
{"Dargwa", "dar", ""},
|
|||
|
{"Dayak", "day", ""},
|
|||
|
{"Delaware", "del", ""},
|
|||
|
{"Dinka", "din", ""},
|
|||
|
{"Divehi", "div", "dv"},
|
|||
|
{"Dogri", "doi", ""},
|
|||
|
{"Dogrib", "dgr", ""},
|
|||
|
{"Dravidian (Other)", "dra", ""},
|
|||
|
{"Duala", "dua", ""},
|
|||
|
{"Dutch; Flemish", "dut", "nl"},
|
|||
|
{"Dutch; Flemish", "nld", "nl"},
|
|||
|
{"Dutch, Middle (ca. 1050-1350)", "dum", ""},
|
|||
|
{"Dyula", "dyu", ""},
|
|||
|
{"Dzongkha", "dzo", "dz"},
|
|||
|
{"Efik", "efi", ""},
|
|||
|
{"Egyptian (Ancient)", "egy", ""},
|
|||
|
{"Ekajuk", "eka", ""},
|
|||
|
{"Elamite", "elx", ""},
|
|||
|
{"English", "eng", "en"},
|
|||
|
{"English, Middle (1100-1500)", "enm", ""},
|
|||
|
{"English, Old (ca.450-1100)", "ang", ""},
|
|||
|
{"Esperanto", "epo", "eo"},
|
|||
|
{"Estonian", "est", "et"},
|
|||
|
{"Ewe", "ewe", "ee"},
|
|||
|
{"Ewondo", "ewo", ""},
|
|||
|
{"Fang", "fan", ""},
|
|||
|
{"Fanti", "fat", ""},
|
|||
|
{"Faroese", "fao", "fo"},
|
|||
|
{"Fijian", "fij", "fj"},
|
|||
|
{"Finnish", "fin", "fi"},
|
|||
|
{"Finno-Ugrian (Other)", "fiu", ""},
|
|||
|
{"Flemish; Dutch", "dut", "nl"},
|
|||
|
{"Flemish; Dutch", "nld", "nl"},
|
|||
|
{"Fon", "fon", ""},
|
|||
|
{"French", "fre", "fr"},
|
|||
|
{"French", "fra*", "fr"},
|
|||
|
{"French, Middle (ca.1400-1600)", "frm", ""},
|
|||
|
{"French, Old (842-ca.1400)", "fro", ""},
|
|||
|
{"Frisian", "fry", "fy"},
|
|||
|
{"Friulian", "fur", ""},
|
|||
|
{"Fulah", "ful", "ff"},
|
|||
|
{"Ga", "gaa", ""},
|
|||
|
{"Gaelic; Scottish Gaelic", "gla", "gd"},
|
|||
|
{"Gallegan", "glg", "gl"},
|
|||
|
{"Ganda", "lug", "lg"},
|
|||
|
{"Gayo", "gay", ""},
|
|||
|
{"Gbaya", "gba", ""},
|
|||
|
{"Geez", "gez", ""},
|
|||
|
{"Georgian", "geo", "ka"},
|
|||
|
{"Georgian", "kat", "ka"},
|
|||
|
{"German", "ger", "de"},
|
|||
|
{"German", "deu", "de"},
|
|||
|
{"German, Low; Saxon, Low; Low German; Low Saxon", "nds", ""},
|
|||
|
{"German, Middle High (ca.1050-1500)", "gmh", ""},
|
|||
|
{"German, Old High (ca.750-1050)", "goh", ""},
|
|||
|
{"Germanic (Other)", "gem", ""},
|
|||
|
{"Gikuyu; Kikuyu", "kik", "ki"},
|
|||
|
{"Gilbertese", "gil", ""},
|
|||
|
{"Gondi", "gon", ""},
|
|||
|
{"Gorontalo", "gor", ""},
|
|||
|
{"Gothic", "got", ""},
|
|||
|
{"Grebo", "grb", ""},
|
|||
|
{"Greek, Ancient (to 1453)", "grc", ""},
|
|||
|
{"Greek, Modern (1453-)", "gre", "el"},
|
|||
|
{"Greek, Modern (1453-)", "ell", "el"},
|
|||
|
{"Greenlandic; Kalaallisut", "kal", "kl"},
|
|||
|
{"Guarani", "grn", "gn"},
|
|||
|
{"Gujarati", "guj", "gu"},
|
|||
|
{"Gwich<EFBFBD>in", "gwi", ""},
|
|||
|
{"Haida", "hai", ""},
|
|||
|
{"Hausa", "hau", "ha"},
|
|||
|
{"Hawaiian", "haw", ""},
|
|||
|
{"Hebrew", "heb", "he"},
|
|||
|
{"Herero", "her", "hz"},
|
|||
|
{"Hiligaynon", "hil", ""},
|
|||
|
{"Himachali", "him", ""},
|
|||
|
{"Hindi", "hin", "hi"},
|
|||
|
{"Hiri Motu", "hmo", "ho"},
|
|||
|
{"Hittite", "hit", ""},
|
|||
|
{"Hmong", "hmn", ""},
|
|||
|
{"Hungarian", "hun", "hu"},
|
|||
|
{"Hupa", "hup", ""},
|
|||
|
{"Iban", "iba", ""},
|
|||
|
{"Icelandic", "ice", "is"},
|
|||
|
{"Icelandic", "isl", "is"},
|
|||
|
{"Ido", "ido", "io"},
|
|||
|
{"Igbo", "ibo", "ig"},
|
|||
|
{"Ijo", "ijo", ""},
|
|||
|
{"Iloko", "ilo", ""},
|
|||
|
{"Inari Sami", "smn", ""},
|
|||
|
{"Indic (Other)", "inc", ""},
|
|||
|
{"Indo-European (Other)", "ine", ""},
|
|||
|
{"Indonesian", "ind", "id"},
|
|||
|
{"Ingush", "inh", ""},
|
|||
|
{"Interlingua (International", "ina", "ia"},
|
|||
|
// {"Auxiliary Language Association)", "", ""},
|
|||
|
{"Interlingue", "ile", "ie"},
|
|||
|
{"Inuktitut", "iku", "iu"},
|
|||
|
{"Inupiaq", "ipk", "ik"},
|
|||
|
{"Iranian (Other)", "ira", ""},
|
|||
|
{"Irish", "gle", "ga"},
|
|||
|
{"Irish, Middle (900-1200)", "mga", ""},
|
|||
|
{"Irish, Old (to 900)", "sga", ""},
|
|||
|
{"Iroquoian languages", "iro", ""},
|
|||
|
{"Italian", "ita", "it"},
|
|||
|
{"Japanese", "jpn", "ja"},
|
|||
|
{"Javanese", "jav", "jv"},
|
|||
|
{"Judeo-Arabic", "jrb", ""},
|
|||
|
{"Judeo-Persian", "jpr", ""},
|
|||
|
{"Kabardian", "kbd", ""},
|
|||
|
{"Kabyle", "kab", ""},
|
|||
|
{"Kachin", "kac", ""},
|
|||
|
{"Kalaallisut; Greenlandic", "kal", "kl"},
|
|||
|
{"Kamba", "kam", ""},
|
|||
|
{"Kannada", "kan", "kn"},
|
|||
|
{"Kanuri", "kau", "kr"},
|
|||
|
{"Kara-Kalpak", "kaa", ""},
|
|||
|
{"Karen", "kar", ""},
|
|||
|
{"Kashmiri", "kas", "ks"},
|
|||
|
{"Kawi", "kaw", ""},
|
|||
|
{"Kazakh", "kaz", "kk"},
|
|||
|
{"Khasi", "kha", ""},
|
|||
|
{"Khmer", "khm", "km"},
|
|||
|
{"Khoisan (Other)", "khi", ""},
|
|||
|
{"Khotanese", "kho", ""},
|
|||
|
{"Kikuyu; Gikuyu", "kik", "ki"},
|
|||
|
{"Kimbundu", "kmb", ""},
|
|||
|
{"Kinyarwanda", "kin", "rw"},
|
|||
|
{"Kirghiz", "kir", "ky"},
|
|||
|
{"Komi", "kom", "kv"},
|
|||
|
{"Kongo", "kon", "kg"},
|
|||
|
{"Konkani", "kok", ""},
|
|||
|
{"Korean", "kor", "ko"},
|
|||
|
{"Kosraean", "kos", ""},
|
|||
|
{"Kpelle", "kpe", ""},
|
|||
|
{"Kru", "kro", ""},
|
|||
|
{"Kuanyama; Kwanyama", "kua", "kj"},
|
|||
|
{"Kumyk", "kum", ""},
|
|||
|
{"Kurdish", "kur", "ku"},
|
|||
|
{"Kurukh", "kru", ""},
|
|||
|
{"Kutenai", "kut", ""},
|
|||
|
{"Kwanyama, Kuanyama", "kua", "kj"},
|
|||
|
{"Ladino", "lad", ""},
|
|||
|
{"Lahnda", "lah", ""},
|
|||
|
{"Lamba", "lam", ""},
|
|||
|
{"Lao", "lao", "lo"},
|
|||
|
{"Latin", "lat", "la"},
|
|||
|
{"Latvian", "lav", "lv"},
|
|||
|
{"Letzeburgesch; Luxembourgish", "ltz", "lb"},
|
|||
|
{"Lezghian", "lez", ""},
|
|||
|
{"Limburgan; Limburger; Limburgish", "lim", "li"},
|
|||
|
{"Limburger; Limburgan; Limburgish;", "lim", "li"},
|
|||
|
{"Limburgish; Limburger; Limburgan", "lim", "li"},
|
|||
|
{"Lingala", "lin", "ln"},
|
|||
|
{"Lithuanian", "lit", "lt"},
|
|||
|
{"Low German; Low Saxon; German, Low; Saxon, Low", "nds", ""},
|
|||
|
{"Low Saxon; Low German; Saxon, Low; German, Low", "nds", ""},
|
|||
|
{"Lozi", "loz", ""},
|
|||
|
{"Luba-Katanga", "lub", "lu"},
|
|||
|
{"Luba-Lulua", "lua", ""},
|
|||
|
{"Luiseno", "lui", ""},
|
|||
|
{"Lule Sami", "smj", ""},
|
|||
|
{"Lunda", "lun", ""},
|
|||
|
{"Luo (Kenya and Tanzania)", "luo", ""},
|
|||
|
{"Lushai", "lus", ""},
|
|||
|
{"Luxembourgish; Letzeburgesch", "ltz", "lb"},
|
|||
|
{"Macedonian", "mac", "mk"},
|
|||
|
{"Macedonian", "mkd", "mk"},
|
|||
|
{"Madurese", "mad", ""},
|
|||
|
{"Magahi", "mag", ""},
|
|||
|
{"Maithili", "mai", ""},
|
|||
|
{"Makasar", "mak", ""},
|
|||
|
{"Malagasy", "mlg", "mg"},
|
|||
|
{"Malay", "may", "ms"},
|
|||
|
{"Malay", "msa", "ms"},
|
|||
|
{"Malayalam", "mal", "ml"},
|
|||
|
{"Maltese", "mlt", "mt"},
|
|||
|
{"Manchu", "mnc", ""},
|
|||
|
{"Mandar", "mdr", ""},
|
|||
|
{"Mandingo", "man", ""},
|
|||
|
{"Manipuri", "mni", ""},
|
|||
|
{"Manobo languages", "mno", ""},
|
|||
|
{"Manx", "glv", "gv"},
|
|||
|
{"Maori", "mao", "mi"},
|
|||
|
{"Maori", "mri", "mi"},
|
|||
|
{"Marathi", "mar", "mr"},
|
|||
|
{"Mari", "chm", ""},
|
|||
|
{"Marshallese", "mah", "mh"},
|
|||
|
{"Marwari", "mwr", ""},
|
|||
|
{"Masai", "mas", ""},
|
|||
|
{"Mayan languages", "myn", ""},
|
|||
|
{"Mende", "men", ""},
|
|||
|
{"Micmac", "mic", ""},
|
|||
|
{"Minangkabau", "min", ""},
|
|||
|
{"Miscellaneous languages", "mis", ""},
|
|||
|
{"Mohawk", "moh", ""},
|
|||
|
{"Moldavian", "mol", "mo"},
|
|||
|
{"Mon-Khmer (Other)", "mkh", ""},
|
|||
|
{"Mongo", "lol", ""},
|
|||
|
{"Mongolian", "mon", "mn"},
|
|||
|
{"Mossi", "mos", ""},
|
|||
|
{"Multiple languages", "mul", ""},
|
|||
|
{"Munda languages", "mun", ""},
|
|||
|
{"Nahuatl", "nah", ""},
|
|||
|
{"Nauru", "nau", "na"},
|
|||
|
{"Navaho, Navajo", "nav", "nv"},
|
|||
|
{"Navajo; Navaho", "nav", "nv"},
|
|||
|
{"Ndebele, North", "nde", "nd"},
|
|||
|
{"Ndebele, South", "nbl", "nr"},
|
|||
|
{"Ndonga", "ndo", "ng"},
|
|||
|
{"Neapolitan", "nap", ""},
|
|||
|
{"Nepali", "nep", "ne"},
|
|||
|
{"Newari", "new", ""},
|
|||
|
{"Nias", "nia", ""},
|
|||
|
{"Niger-Kordofanian (Other)", "nic", ""},
|
|||
|
{"Nilo-Saharan (Other)", "ssa", ""},
|
|||
|
{"Niuean", "niu", ""},
|
|||
|
{"Norse, Old", "non", ""},
|
|||
|
{"North American Indian (Other)", "nai", ""},
|
|||
|
{"Northern Sami", "sme", "se"},
|
|||
|
{"North Ndebele", "nde", "nd"},
|
|||
|
{"Norwegian", "nor", "no"},
|
|||
|
{"Norwegian Bokm<6B>l; Bokm<6B>l, Norwegian", "nob", "nb"},
|
|||
|
{"Norwegian Nynorsk; Nynorsk, Norwegian", "nno", "nn"},
|
|||
|
{"Nubian languages", "nub", ""},
|
|||
|
{"Nyamwezi", "nym", ""},
|
|||
|
{"Nyanja; Chichewa; Chewa", "nya", "ny"},
|
|||
|
{"Nyankole", "nyn", ""},
|
|||
|
{"Nynorsk, Norwegian; Norwegian Nynorsk", "nno", "nn"},
|
|||
|
{"Nyoro", "nyo", ""},
|
|||
|
{"Nzima", "nzi", ""},
|
|||
|
{"Occitan (post 1500},; Proven<65>al", "oci", "oc"},
|
|||
|
{"Ojibwa", "oji", "oj"},
|
|||
|
{"Old Bulgarian; Old Slavonic; Church Slavonic;", "chu", "cu"},
|
|||
|
{"Oriya", "ori", "or"},
|
|||
|
{"Oromo", "orm", "om"},
|
|||
|
{"Osage", "osa", ""},
|
|||
|
{"Ossetian; Ossetic", "oss", "os"},
|
|||
|
{"Ossetic; Ossetian", "oss", "os"},
|
|||
|
{"Otomian languages", "oto", ""},
|
|||
|
{"Pahlavi", "pal", ""},
|
|||
|
{"Palauan", "pau", ""},
|
|||
|
{"Pali", "pli", "pi"},
|
|||
|
{"Pampanga", "pam", ""},
|
|||
|
{"Pangasinan", "pag", ""},
|
|||
|
{"Panjabi", "pan", "pa"},
|
|||
|
{"Papiamento", "pap", ""},
|
|||
|
{"Papuan (Other)", "paa", ""},
|
|||
|
{"Persian", "per", "fa"},
|
|||
|
{"Persian", "fas", "fa"},
|
|||
|
{"Persian, Old (ca.600-400 B.C.)", "peo", ""},
|
|||
|
{"Philippine (Other)", "phi", ""},
|
|||
|
{"Phoenician", "phn", ""},
|
|||
|
{"Pohnpeian", "pon", ""},
|
|||
|
{"Polish", "pol", "pl"},
|
|||
|
{"Portuguese", "por", "pt"},
|
|||
|
{"Prakrit languages", "pra", ""},
|
|||
|
{"Proven<EFBFBD>al; Occitan (post 1500)", "oci", "oc"},
|
|||
|
{"Proven<EFBFBD>al, Old (to 1500)", "pro", ""},
|
|||
|
{"Pushto", "pus", "ps"},
|
|||
|
{"Quechua", "que", "qu"},
|
|||
|
{"Raeto-Romance", "roh", "rm"},
|
|||
|
{"Rajasthani", "raj", ""},
|
|||
|
{"Rapanui", "rap", ""},
|
|||
|
{"Rarotongan", "rar", ""},
|
|||
|
{"Reserved for local use", "qaa-qtz", ""},
|
|||
|
{"Romance (Other)", "roa", ""},
|
|||
|
{"Romanian", "rum", "ro"},
|
|||
|
{"Romanian", "ron", "ro"},
|
|||
|
{"Romany", "rom", ""},
|
|||
|
{"Rundi", "run", "rn"},
|
|||
|
{"Russian", "rus", "ru"},
|
|||
|
{"Salishan languages", "sal", ""},
|
|||
|
{"Samaritan Aramaic", "sam", ""},
|
|||
|
{"Sami languages (Other)", "smi", ""},
|
|||
|
{"Samoan", "smo", "sm"},
|
|||
|
{"Sandawe", "sad", ""},
|
|||
|
{"Sango", "sag", "sg"},
|
|||
|
{"Sanskrit", "san", "sa"},
|
|||
|
{"Santali", "sat", ""},
|
|||
|
{"Sardinian", "srd", "sc"},
|
|||
|
{"Sasak", "sas", ""},
|
|||
|
{"Saxon, Low; German, Low; Low Saxon; Low German", "nds", ""},
|
|||
|
{"Scots", "sco", ""},
|
|||
|
{"Scottish Gaelic; Gaelic", "gla", "gd"},
|
|||
|
{"Selkup", "sel", ""},
|
|||
|
{"Semitic (Other)", "sem", ""},
|
|||
|
{"Serbian", "scc", "sr"},
|
|||
|
{"Serbian", "srp", "sr"},
|
|||
|
{"Serer", "srr", ""},
|
|||
|
{"Shan", "shn", ""},
|
|||
|
{"Shona", "sna", "sn"},
|
|||
|
{"Sichuan Yi", "iii", "ii"},
|
|||
|
{"Sidamo", "sid", ""},
|
|||
|
{"Sign languages", "sgn", ""},
|
|||
|
{"Siksika", "bla", ""},
|
|||
|
{"Sindhi", "snd", "sd"},
|
|||
|
{"Sinhalese", "sin", "si"},
|
|||
|
{"Sino-Tibetan (Other)", "sit", ""},
|
|||
|
{"Siouan languages", "sio", ""},
|
|||
|
{"Skolt Sami", "sms", ""},
|
|||
|
{"Slave (Athapascan)", "den", ""},
|
|||
|
{"Slavic (Other)", "sla", ""},
|
|||
|
{"Slovak", "slo", "sk"},
|
|||
|
{"Slovak", "slk", "sk"},
|
|||
|
{"Slovenian", "slv", "sl"},
|
|||
|
{"Sogdian", "sog", ""},
|
|||
|
{"Somali", "som", "so"},
|
|||
|
{"Songhai", "son", ""},
|
|||
|
{"Soninke", "snk", ""},
|
|||
|
{"Sorbian languages", "wen", ""},
|
|||
|
{"Sotho, Northern", "nso", ""},
|
|||
|
{"Sotho, Southern", "sot", "st"},
|
|||
|
{"South American Indian (Other)", "sai", ""},
|
|||
|
{"Southern Sami", "sma", ""},
|
|||
|
{"South Ndebele", "nbl", "nr"},
|
|||
|
{"Spanish; Castilian", "spa", "es"},
|
|||
|
{"Sukuma", "suk", ""},
|
|||
|
{"Sumerian", "sux", ""},
|
|||
|
{"Sundanese", "sun", "su"},
|
|||
|
{"Susu", "sus", ""},
|
|||
|
{"Swahili", "swa", "sw"},
|
|||
|
{"Swati", "ssw", "ss"},
|
|||
|
{"Swedish", "swe", "sv"},
|
|||
|
{"Syriac", "syr", ""},
|
|||
|
{"Tagalog", "tgl", "tl"},
|
|||
|
{"Tahitian", "tah", "ty"},
|
|||
|
{"Tai (Other)", "tai", ""},
|
|||
|
{"Tajik", "tgk", "tg"},
|
|||
|
{"Tamashek", "tmh", ""},
|
|||
|
{"Tamil", "tam", "ta"},
|
|||
|
{"Tatar", "tat", "tt"},
|
|||
|
{"Telugu", "tel", "te"},
|
|||
|
{"Tereno", "ter", ""},
|
|||
|
{"Tetum", "tet", ""},
|
|||
|
{"Thai", "tha", "th"},
|
|||
|
{"Tibetan", "tib", "bo"},
|
|||
|
{"Tibetan", "bod", "bo"},
|
|||
|
{"Tigre", "tig", ""},
|
|||
|
{"Tigrinya", "tir", "ti"},
|
|||
|
{"Timne", "tem", ""},
|
|||
|
{"Tiv", "tiv", ""},
|
|||
|
{"Tlingit", "tli", ""},
|
|||
|
{"Tok Pisin", "tpi", ""},
|
|||
|
{"Tokelau", "tkl", ""},
|
|||
|
{"Tonga (Nyasa)", "tog", ""},
|
|||
|
{"Tonga (Tonga Islands)", "ton", "to"},
|
|||
|
{"Tsimshian", "tsi", ""},
|
|||
|
{"Tsonga", "tso", "ts"},
|
|||
|
{"Tswana", "tsn", "tn"},
|
|||
|
{"Tumbuka", "tum", ""},
|
|||
|
{"Tupi languages", "tup", ""},
|
|||
|
{"Turkish", "tur", "tr"},
|
|||
|
{"Turkish, Ottoman (1500-1928)", "ota", ""},
|
|||
|
{"Turkmen", "tuk", "tk"},
|
|||
|
{"Tuvalu", "tvl", ""},
|
|||
|
{"Tuvinian", "tyv", ""},
|
|||
|
{"Twi", "twi", "tw"},
|
|||
|
{"Ugaritic", "uga", ""},
|
|||
|
{"Uighur", "uig", "ug"},
|
|||
|
{"Ukrainian", "ukr", "uk"},
|
|||
|
{"Umbundu", "umb", ""},
|
|||
|
{"Undetermined", "und", ""},
|
|||
|
{"Urdu", "urd", "ur"},
|
|||
|
{"Uzbek", "uzb", "uz"},
|
|||
|
{"Vai", "vai", ""},
|
|||
|
{"Venda", "ven", "ve"},
|
|||
|
{"Vietnamese", "vie", "vi"},
|
|||
|
{"Volap<EFBFBD>k", "vol", "vo"},
|
|||
|
{"Votic", "vot", ""},
|
|||
|
{"Wakashan languages", "wak", ""},
|
|||
|
{"Walamo", "wal", ""},
|
|||
|
{"Walloon", "wln", "wa"},
|
|||
|
{"Waray", "war", ""},
|
|||
|
{"Washo", "was", ""},
|
|||
|
{"Welsh", "wel", "cy"},
|
|||
|
{"Welsh", "cym", "cy"},
|
|||
|
{"Wolof", "wol", "wo"},
|
|||
|
{"Xhosa", "xho", "xh"},
|
|||
|
{"Yakut", "sah", ""},
|
|||
|
{"Yao", "yao", ""},
|
|||
|
{"Yapese", "yap", ""},
|
|||
|
{"Yiddish", "yid", "yi"},
|
|||
|
{"Yoruba", "yor", "yo"},
|
|||
|
{"Yupik languages", "ypk", ""},
|
|||
|
{"Zande", "znd", ""},
|
|||
|
{"Zapotec", "zap", ""},
|
|||
|
{"Zenaga", "zen", ""},
|
|||
|
{"Zhuang; Chuang", "zha", "za"},
|
|||
|
{"Zulu", "zul", "zu"},
|
|||
|
{"Zuni", "zun", ""},
|
|||
|
{"Classical Newari", "nwc", ""},
|
|||
|
{"Klingon", "tlh", ""},
|
|||
|
{"Blin", "byn", ""},
|
|||
|
{"Lojban", "jbo", ""},
|
|||
|
{"Lower Sorbian", "dsb", ""},
|
|||
|
{"Upper Sorbian", "hsb", ""},
|
|||
|
{"Kashubian", "csb", ""},
|
|||
|
{"Crimean Turkish", "crh", ""},
|
|||
|
{"Erzya", "myv", ""},
|
|||
|
{"Moksha", "mdf", ""},
|
|||
|
{"Karachay-Balkar", "krc", ""},
|
|||
|
{"Adyghe", "ady", ""},
|
|||
|
{"Udmurt", "udm", ""},
|
|||
|
{"Dargwa", "dar", ""},
|
|||
|
{"Ingush", "inh", ""},
|
|||
|
{"Nogai", "nog", ""},
|
|||
|
{"Haitian", "hat", "ht"},
|
|||
|
{"Kalmyk", "xal", ""},
|
|||
|
{"", "", ""},
|
|||
|
};
|
|||
|
|
|||
|
CString ISO6391ToLanguage(LPCSTR code)
|
|||
|
{
|
|||
|
CHAR tmp[2+1];
|
|||
|
strncpy(tmp, code, 2);
|
|||
|
tmp[2] = 0;
|
|||
|
_strlwr(tmp);
|
|||
|
for(int i = 0, j = countof(s_isolangs); i < j; i++)
|
|||
|
if(!strcmp(s_isolangs[i].iso6391, tmp))
|
|||
|
{
|
|||
|
CString ret = CString(CStringA(s_isolangs[i].name));
|
|||
|
int i = ret.Find(';');
|
|||
|
if(i > 0) ret = ret.Left(i);
|
|||
|
return ret;
|
|||
|
}
|
|||
|
return(_T(""));
|
|||
|
}
|
|||
|
|
|||
|
CString ISO6392ToLanguage(LPCSTR code)
|
|||
|
{
|
|||
|
CHAR tmp[3+1];
|
|||
|
strncpy(tmp, code, 3);
|
|||
|
tmp[3] = 0;
|
|||
|
_strlwr(tmp);
|
|||
|
for(int i = 0, j = countof(s_isolangs); i < j; i++)
|
|||
|
{
|
|||
|
if(!strcmp(s_isolangs[i].iso6392, tmp))
|
|||
|
{
|
|||
|
CString ret = CString(CStringA(s_isolangs[i].name));
|
|||
|
int i = ret.Find(';');
|
|||
|
if(i > 0) ret = ret.Left(i);
|
|||
|
return ret;
|
|||
|
}
|
|||
|
}
|
|||
|
return _T("");
|
|||
|
}
|
|||
|
|
|||
|
CString ISO6391To6392(LPCSTR code)
|
|||
|
{
|
|||
|
CHAR tmp[2+1];
|
|||
|
strncpy(tmp, code, 2);
|
|||
|
tmp[2] = 0;
|
|||
|
_strlwr(tmp);
|
|||
|
for(int i = 0, j = countof(s_isolangs); i < j; i++)
|
|||
|
if(!strcmp(s_isolangs[i].iso6391, tmp))
|
|||
|
return CString(CStringA(s_isolangs[i].iso6392));
|
|||
|
return _T("");
|
|||
|
}
|
|||
|
|
|||
|
CString ISO6392To6391(LPCSTR code)
|
|||
|
{
|
|||
|
CHAR tmp[3+1];
|
|||
|
strncpy(tmp, code, 3);
|
|||
|
tmp[3] = 0;
|
|||
|
_strlwr(tmp);
|
|||
|
for(int i = 0, j = countof(s_isolangs); i < j; i++)
|
|||
|
if(!strcmp(s_isolangs[i].iso6392, tmp))
|
|||
|
return CString(CStringA(s_isolangs[i].iso6391));
|
|||
|
return _T("");
|
|||
|
}
|
|||
|
|
|||
|
CString LanguageToISO6392(LPCTSTR lang)
|
|||
|
{
|
|||
|
CString str = lang;
|
|||
|
str.MakeLower();
|
|||
|
for(int i = 0, j = countof(s_isolangs); i < j; i++)
|
|||
|
{
|
|||
|
CAtlList<CString> sl;
|
|||
|
Explode(CString(s_isolangs[i].name), sl, ';');
|
|||
|
POSITION pos = sl.GetHeadPosition();
|
|||
|
while(pos)
|
|||
|
{
|
|||
|
if(!str.CompareNoCase(sl.GetNext(pos)))
|
|||
|
return CString(s_isolangs[i].iso6392);
|
|||
|
}
|
|||
|
}
|
|||
|
return _T("");
|
|||
|
}
|
|||
|
|
|||
|
int MakeAACInitData(BYTE* pData, int profile, int freq, int channels)
|
|||
|
{
|
|||
|
int srate_idx;
|
|||
|
|
|||
|
if(92017 <= freq) srate_idx = 0;
|
|||
|
else if(75132 <= freq) srate_idx = 1;
|
|||
|
else if(55426 <= freq) srate_idx = 2;
|
|||
|
else if(46009 <= freq) srate_idx = 3;
|
|||
|
else if(37566 <= freq) srate_idx = 4;
|
|||
|
else if(27713 <= freq) srate_idx = 5;
|
|||
|
else if(23004 <= freq) srate_idx = 6;
|
|||
|
else if(18783 <= freq) srate_idx = 7;
|
|||
|
else if(13856 <= freq) srate_idx = 8;
|
|||
|
else if(11502 <= freq) srate_idx = 9;
|
|||
|
else if(9391 <= freq) srate_idx = 10;
|
|||
|
else srate_idx = 11;
|
|||
|
|
|||
|
pData[0] = ((abs(profile) + 1) << 3) | ((srate_idx & 0xe) >> 1);
|
|||
|
pData[1] = ((srate_idx & 0x1) << 7) | (channels << 3);
|
|||
|
|
|||
|
int ret = 2;
|
|||
|
|
|||
|
if(profile < 0)
|
|||
|
{
|
|||
|
freq *= 2;
|
|||
|
|
|||
|
if(92017 <= freq) srate_idx = 0;
|
|||
|
else if(75132 <= freq) srate_idx = 1;
|
|||
|
else if(55426 <= freq) srate_idx = 2;
|
|||
|
else if(46009 <= freq) srate_idx = 3;
|
|||
|
else if(37566 <= freq) srate_idx = 4;
|
|||
|
else if(27713 <= freq) srate_idx = 5;
|
|||
|
else if(23004 <= freq) srate_idx = 6;
|
|||
|
else if(18783 <= freq) srate_idx = 7;
|
|||
|
else if(13856 <= freq) srate_idx = 8;
|
|||
|
else if(11502 <= freq) srate_idx = 9;
|
|||
|
else if(9391 <= freq) srate_idx = 10;
|
|||
|
else srate_idx = 11;
|
|||
|
|
|||
|
pData[2] = 0x2B7>>3;
|
|||
|
pData[3] = (BYTE)((0x2B7<<5) | 5);
|
|||
|
pData[4] = (1<<7) | (srate_idx<<3);
|
|||
|
|
|||
|
ret = 5;
|
|||
|
}
|
|||
|
|
|||
|
return(ret);
|
|||
|
}
|
|||
|
|
|||
|
BOOL CFileGetStatus(LPCTSTR lpszFileName, CFileStatus& status)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
return CFile::GetStatus(lpszFileName, status);
|
|||
|
}
|
|||
|
catch(CException* e)
|
|||
|
{
|
|||
|
// MFCBUG: E_INVALIDARG / "Parameter is incorrect" is thrown for certain cds (vs2003)
|
|||
|
// http://groups.google.co.uk/groups?hl=en&lr=&ie=UTF-8&threadm=OZuXYRzWDHA.536%40TK2MSFTNGP10.phx.gbl&rnum=1&prev=/groups%3Fhl%3Den%26lr%3D%26ie%3DISO-8859-1
|
|||
|
TRACE(_T("CFile::GetStatus has thrown an exception\n"));
|
|||
|
e->Delete();
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// filter registration helpers
|
|||
|
|
|||
|
bool DeleteRegKey(LPCTSTR pszKey, LPCTSTR pszSubkey)
|
|||
|
{
|
|||
|
bool bOK = false;
|
|||
|
|
|||
|
HKEY hKey;
|
|||
|
LONG ec = ::RegOpenKeyEx(HKEY_CLASSES_ROOT, pszKey, 0, KEY_ALL_ACCESS, &hKey);
|
|||
|
if(ec == ERROR_SUCCESS)
|
|||
|
{
|
|||
|
if(pszSubkey != 0)
|
|||
|
ec = ::RegDeleteKey(hKey, pszSubkey);
|
|||
|
|
|||
|
bOK = (ec == ERROR_SUCCESS);
|
|||
|
|
|||
|
::RegCloseKey(hKey);
|
|||
|
}
|
|||
|
|
|||
|
return bOK;
|
|||
|
}
|
|||
|
|
|||
|
bool SetRegKeyValue(LPCTSTR pszKey, LPCTSTR pszSubkey, LPCTSTR pszValueName, LPCTSTR pszValue)
|
|||
|
{
|
|||
|
bool bOK = false;
|
|||
|
|
|||
|
CString szKey(pszKey);
|
|||
|
if(pszSubkey != 0)
|
|||
|
szKey += CString(_T("\\")) + pszSubkey;
|
|||
|
|
|||
|
HKEY hKey;
|
|||
|
LONG ec = ::RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, 0);
|
|||
|
if(ec == ERROR_SUCCESS)
|
|||
|
{
|
|||
|
if(pszValue != 0)
|
|||
|
{
|
|||
|
ec = ::RegSetValueEx(hKey, pszValueName, 0, REG_SZ,
|
|||
|
reinterpret_cast<BYTE*>(const_cast<LPTSTR>(pszValue)),
|
|||
|
(_tcslen(pszValue) + 1) * sizeof(TCHAR));
|
|||
|
}
|
|||
|
|
|||
|
bOK = (ec == ERROR_SUCCESS);
|
|||
|
|
|||
|
::RegCloseKey(hKey);
|
|||
|
}
|
|||
|
|
|||
|
return bOK;
|
|||
|
}
|
|||
|
|
|||
|
bool SetRegKeyValue(LPCTSTR pszKey, LPCTSTR pszSubkey, LPCTSTR pszValue)
|
|||
|
{
|
|||
|
return SetRegKeyValue(pszKey, pszSubkey, 0, pszValue);
|
|||
|
}
|
|||
|
|
|||
|
void RegisterSourceFilter(const CLSID& clsid, const GUID& subtype2, LPCTSTR chkbytes, LPCTSTR ext, ...)
|
|||
|
{
|
|||
|
CString null = CStringFromGUID(GUID_NULL);
|
|||
|
CString majortype = CStringFromGUID(MEDIATYPE_Stream);
|
|||
|
CString subtype = CStringFromGUID(subtype2);
|
|||
|
|
|||
|
SetRegKeyValue(_T("Media Type\\") + majortype, subtype, _T("0"), chkbytes);
|
|||
|
SetRegKeyValue(_T("Media Type\\") + majortype, subtype, _T("Source Filter"), CStringFromGUID(clsid));
|
|||
|
|
|||
|
DeleteRegKey(_T("Media Type\\") + null, subtype);
|
|||
|
|
|||
|
va_list marker;
|
|||
|
va_start(marker, ext);
|
|||
|
for(; ext; ext = va_arg(marker, LPCTSTR))
|
|||
|
DeleteRegKey(_T("Media Type\\Extensions"), ext);
|
|||
|
va_end(marker);
|
|||
|
}
|
|||
|
|
|||
|
void RegisterSourceFilter(const CLSID& clsid, const GUID& subtype2, const CAtlList<CString>& chkbytes, LPCTSTR ext, ...)
|
|||
|
{
|
|||
|
CString null = CStringFromGUID(GUID_NULL);
|
|||
|
CString majortype = CStringFromGUID(MEDIATYPE_Stream);
|
|||
|
CString subtype = CStringFromGUID(subtype2);
|
|||
|
|
|||
|
POSITION pos = chkbytes.GetHeadPosition();
|
|||
|
for(int i = 0; pos; i++)
|
|||
|
{
|
|||
|
CString idx;
|
|||
|
idx.Format(_T("%d"), i);
|
|||
|
SetRegKeyValue(_T("Media Type\\") + majortype, subtype, idx, chkbytes.GetNext(pos));
|
|||
|
}
|
|||
|
|
|||
|
SetRegKeyValue(_T("Media Type\\") + majortype, subtype, _T("Source Filter"), CStringFromGUID(clsid));
|
|||
|
|
|||
|
DeleteRegKey(_T("Media Type\\") + null, subtype);
|
|||
|
|
|||
|
va_list marker;
|
|||
|
va_start(marker, ext);
|
|||
|
for(; ext; ext = va_arg(marker, LPCTSTR))
|
|||
|
DeleteRegKey(_T("Media Type\\Extensions"), ext);
|
|||
|
va_end(marker);
|
|||
|
}
|
|||
|
|
|||
|
void UnRegisterSourceFilter(const GUID& subtype)
|
|||
|
{
|
|||
|
DeleteRegKey(_T("Media Type\\") + CStringFromGUID(MEDIATYPE_Stream), CStringFromGUID(subtype));
|
|||
|
}
|