这是很久以前的东西,如果有不准确的地方还请见谅
DVD的解码
本节主要包括DirectShow属性设置和特定DVD及扩展解码所用的接口描述,此外还有支持通用 DirectShow 过滤器接口的解码器及其管脚属性描述。
本节主要包含以下主题:
l 解码器音量控制
l Windows 支持的DVD区码变换
以及:
l DVD Karaoke 属性设置
l DVD 复制保护属性设置
l DVD 子图属性设置
l 管脚属性设置
l IKsPropertySet 接口
l IVideoFrameStep 接口(硬件解码器专用)
l IVPConfig 接口(硬件解码器专用)
1、音量控制
应用程序通过iBasicAudio接口管理音量控制,而iBasicAudio接口是由KSProxy进行管理,所以为了使解码器处理这些命令,就必须在KSPPROPSETID_Wave属性设置中增加一些注册键。下来我们就来创建这些新的驱动注册键:
HKLM\SYSTEM CurrentControlSet\Control
DeviceClasses
(decoder guid, eg 2721AE....)
(Pnp id, eg ##?#VDGENDEV#...)
#GLOBAL
Device Parameters
CLSID REG_SZ {17CCA...}
FriendlyName REG_SZ WDM DVD Driver
Interfaces <--- create this key
{b9f8ac3e-0f71-11d2-b72c-00c04fb6bd3d} <-- also create this key, it is not a value)
MediaInterfaces
{b9f8ac3e-0f71-11d2-b72c-00c04fb6bd3d} <-- create this key
(default) REG_SZ 'KsProxy IBasicAudio handler' <-- set this value
IID REG_SZ 56 a8 68 b3 0a d4 11 ce b0 3a 00 20 af 0b a7 70 <-- create this string value
实现音量控制,驱动程序必需支持KSPROPSETID_Wave及KsProperty.Id=KSPROPERTY_WAVE_VOLUME。该属性是驱动程序通过IksPropertySet::Get和IksPropertySet::Set方法进行控制的。左、右音频控制的音量大小是0x0000到0xffff的线性值 。
2、DVD的区码变换
本段解释windows9x和windows2000下选择DVD区码的工作过程。首先描述三种DVD区码信息的来源,随后描述PC厂商如何设置系统进行DVD区码初始化,接着解释DVD导航怎样通过驱动、解码器和碟片来决定区码的匹配,最后揭示应用程序和解码器开发者怎样才能实现正确的DVD区码选择。
2.1、DVD区码信息的来源
DVD区码信息有三种来源,这些来源一起就决定了windows的DVD播放器区码。
DVD标题
大部分DVD标题被标记为特定区码。有些标题只能在某个区域播放,但另一些却可以在很多区域播放,还有的可以全区域播放。
DVD-ROM驱动器
有两种DVD-ROM驱动器:RPC 1和RPC 2。RPC 1驱动器没有内置硬件支持的区码管理,所以对于这些驱动器来说,需要由Windows来管理区码改变次数信息,且只能改变一次;而RPC 2驱动器将这些管理放到了硬件里,因此这些驱动器的区码可以被改变5次。
DVD解码器
一些DVD解码器,无论硬件或者软件,都被设置为特定区码服务。通常情况下,用户不能改变解码器本身设置的区码。
2.2、如何在Windows98和Windows 2000中设置初始化区码
此问题主要是DVD驱动器生产商的责任,他们会在DVD驱动器出厂之前选择一个合适的区码。在Windows98中,生产商可以通过设定注册键值HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Current\Version\Default DVD的区码(二进制)来确定DVD驱动器默认区码。该值决定Windows中DVD解码的首选解码器,如果此值在系统DVD播放之前被设定,那么Windows组件会自动选择该解码器进行DVD播放。
如果生产商在注册表中设定了默认的区码,然后却播放不同区码的碟片(包括默认区码DVD驱动器),用户将会被提示需要改变所要求的区码。如果厂商没有指定默认的区码,那么Windows将选择一个基于操作系统的本地区码、时区和其它因素。如果第一张碟片是多区码碟片,那么Windows将还将寻找相匹配的最小区码数。如果找不到相匹配的区码,则选择的区码就是最小区码数最小的那个区码。
如果生产商没有设置区码,则第一次启动Windows 98操作系统基本组件时,将选择基于操作系统的语言、时区并设置驱动器为此种区码。生产厂家设置区码为“全部适应”,如果厂家没有成这些处理完,则最好选择推荐区码。
在Windows 2000下,默认DVD区码选择基于机器的设定。如果有碟片在驱动器中,Windows 2000通常使用默认和碟片相同的区码为DVD驱动器设置初始化区码,因此在Windows 2000 下 DVD驱动器生产商可以不为初始化DVD驱动器做任何事情。
对于Rpc1的驱动器,如果Windows启动时驱动器中没有DVD碟片,那么将会基于本地机器创建默认区码;如果有碟片在驱动器中,那么将选择驱动器的区码为默认区码,即驱动器中的碟片的区码,否则碟片的最低区码将被用来初始化驱动器的区码。万一默认值不正确,那么用户也有一次改变区码的权利。
对于Rpc2驱动器,如果在处理设置的时,没有对驱动器进行任何区码设置,将会试图按照以上的方法进行选择区码,但是要求驱动器中有碟片。(Rpc1可以选择区码而不要求碟片放在驱动器中。)一旦Rpc2驱动器的区码被设置,那么就无法通过重装windows或者卸载操作系统来自动改变区码了。
3、微软的DVD导航
在pc上播放DVD碟片时,微软的DVD导航使用以下方法来决定区码匹配:DVD导航获取碟片的区码、驱动器区码和解码器的区码。如果碟片区码是全区域的,则DVD导航将进行无条件播放;如果碟片不是全区域区码,DVD导航会检测解码器是否有预先设定区码;如果解码器包含的区码与碟片区码不匹配,则DVD导航会返回错误信息,以表明不能用当前DVD配置播放碟片;如果解码器区码与碟片区码相同,则DVD导航会继续检测驱动器区码是否与碟片区码相同,若相同,则DVD导航播放碟片。不同,则DVD导航调用代码来改变驱动器的区码;若允许改变区码的次数被用尽,则改变区码被视为失败,将不能进行播放。
4、DVD区码改变的发展
在Windows 9x系统中,当DVD导航检测到驱动器必要的区码改变时,使用DVDrgn.exe请求驱动器进行区码的改变。应用程序和操作系统的其它组件被分别安装到目标系统。示范方法如下:
在Windows 2000中,DVD导航调用设备管理中DVD-ROM驱动器设备的属性页。当区码需要被改变的时候,该属性页由DVDplay.exe应用程序产生。此属性页的用户界面非常类似DVDrgn.exe的,可被操作系统识别。
对于Rpc1驱动器,由Windows操作系统部件管理区码的改变;而Rpc2驱动器在其硬件中维持区码设置,在Rpc2驱动器中,当用尽允许改变的次数时,驱动器调用区码改变就会失败,操作系统的区码改变组件将会给用户这些情况和结果的说明。
5、安装支持Windows改变区码的部件
应用程序开发者可以利用DVD区码改变支持,在DVD应用程序中进行区码的改变。这里提供处理此问题的两种不同方法。
5.1、Windows 9x下
在Windows 9x 平台下,区码改变应用程序DVDrgn.exe,只有安装硬件解码和wdm的mini驱动后才被安装,且使用标准的inf文件。
对于软件解码器,可在inf文件中插入以下几行。(这是已安装程序加载DVDrgn.exe到目标系统的方法)
[Version]
Signature=$CHICAGO$
LayoutFile=layout.inf
[DestinationDirs]
DVDRgnCopy=25 ; Windows
DefaultDestDir=25 ; Windows
[DefaultInstall]
CopyFiles=DVDRgnCopy
[DVDRgnCopy]
DVDrgn.exe
5.2、Windows 2000 下
在Windows 2000 系统下,组件在Windows目录下的system32下的storprop.dll文件中提供了DVD区码改变的支持。此dll是通过默认方式安装的,没有额外的步骤来确认它的有效性。
6、在应用程序中加入整合区码改变支持
下面的代码为软件开发者提供了在DVD应用程序中加入DVD区码变化的代码:
/////////////////////////////////////////////////////////////////////
// ChangeDVDRegion : Function to change the DVD drive region.
//
// Parameters:
// In: hWnd - window handle of the application
// that calls this function
// Out: none.
//
// Returns:
// TRUE: if the drive region change is successful
// FALSE: if drive region change fails (probably because
// no drive was found with a valid DVD-V disc).
/////////////////////////////////////////////////////////////////////
BOOL ChangeDVDRegion(HWND hWnd)
{
typedef BOOL (APIENTRY *DVDPPLAUNCHER) (HWND hWnd, CHAR DriveLetter);
BOOL
bRegionChanged = FALSE;
TCHAR szCmdLine[MAX_PATH];
// First find out which drive is a DVD drive with a valid DVD-V disc.
TCHAR szDVDDrive[4];
if (! GetDriveLetter (szDVDDrive) )
{
MessageBox(hWnd,
TEXT("No DVD drive was found with DVD-V disc.")
TEXT("\nCannot change DVD region of the drive."),
TEXT("Error"), MB_OK | MB_INFORMATION) ;
return FALSE;
}
// Detect which OS we are running on. For Windows NT, use the storprop.dll,
// while for Windows 9x use the DVDRgn.exe application to change the region.
OSVERSIONINFO ver;
ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&ver);
if (VER_PLATFORM_WIN32_NT == ver.dwPlatformId)
{
// Windows NT platform
HINSTANCE hInstDLL;
DVDPPLAUNCHER DVDPPLauncher;
CHAR szDVDDriveA[4];
#ifdef UNICODE
WideCharToMultiByte(0, 0, szDVDDrive, -1,
szDVDDriveA, sizeof(szDVDDriveA),
NULL, NULL );
#else
strcpy(szDVDDriveA, szDVDDrive);
#endif // UNICODE
GetSystemDirectory(szCmdLine, MAX_PATH);
lstrcat(szCmdLine, TEXT("\\storprop.dll"));
hInstDLL = LoadLibrary (szCmdLine);
if (hInstDLL)
{
DVDPPLauncher = (DVDPPLAUNCHER) GetProcAddress(hInstDLL,
"DVDLauncher");
if (DVDPPLauncher)
{
bRegionChanged = DVDPPLauncher(hWnd, szDVDDriveA[0]);
}
FreeLibrary(hInstDLL);
}
} // end of region change code for Windows NT platform
else // Windows 9x platform
{
// Get path of \windows\DVDrgn.exe for command line string
GetWindowsDirectory(szCmdLine, MAX_PATH);
lstrcat(szCmdLine, TEXT("\\DVDRgn.exe "));
// Add only the drive letter as command line parameter
lstrncat(szCmdLine, szDVDDrive, 1);
// Prepare to execute DVDRgn.exe
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInfo;
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow = SW_SHOWNORMAL;
StartupInfo.lpReserved = NULL;
StartupInfo.lpDesktop = NULL;
StartupInfo.lpTitle = NULL;
StartupInfo.cbReserved2 = 0;
StartupInfo.lpReserved2 = NULL;
if (CreateProcess(csModuleName, szCmdLine,
NULL, NULL, TRUE,
NORMAL_PRIORITY_CLASS,
NULL, NULL, &StartupInfo, &ProcessInfo) )
{
// Wait until DVDRgn.exe finishes
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
DWORD dwRet = 1;
BOOL bRet = GetExitCodeProcess(ProcessInfo.hProcess,
&dwRet);
// If the user changed the drive region
// successfully, the exit code of DVDRgn.exe is 0.
if (bRet && 0 == dwRet)
{
bRegionChanged = TRUE;
}
}
} // end of region change code for Windows 9x platform
if (bRegionChanged) // if region changed successfully
{
// Delete old filter graph and create new filter graph
// via IDVDGraphBuilder::RenderDVDVideoVolume().
}
else // DVD region didn't happen
{
MessageBox(hWnd,
TEXT("DVD drive region could not be changed.")
TEXT("Error"), MB_OK | MB_INFORMATION) ;
}
} //End function ChangeDVDRegion
/////////////////////////////////////////////////////////////////////
// GetDriveLetter : Function to get the drive letter of the currently
// active DVD drive
// Parameters:
// In: pDVDC - pointer to IDVDControl interface of
// DVDNavigator filter..
// Out: pszDrive - the first DVD drive that is found
// to have a valid DVD-V disc.
//
// Returns:
// TRUE: if a DVD drive is found (with valid disc)
// FALSE: if no DVD drive was found with valid DVD-V disc.
/////////////////////////////////////////////////////////////////////
BOOL GetDriveLetter(IDVDControl *pDVDC, TCHAR *pszDrive)
{
CHAR szPathA[MAX_PATH];
TCHAR szPath[MAX_PATH];
ULONG ulActualSize;
pszDrive[0] = pszDrive[3] = 0;
// Get the current root directory
if (pDVDC ->GetRoot(szPathA, MAX_PATH, &ulActualSize))
{
#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0, szPathA, 0, szPath, MAX_PATH);
#else
lstrcpy(szPath, szPathA);
#endif // UNICODE
lstrcpyn(pszDrive, szPath, 3);
if (DRIVE_CDROM == GetDriveType(pszDrive)) // could be a DVD drive
return TRUE;
}
// Now loop through all the valid drives to detect which one
// is a DVD drive with a valid DVD-V disc in it.
// Get all valid drives
DWORD dwLeng = GetLogicalDriveStrings(MAX_PATH, szPath);
TCHAR *pszTemp = szPath;
// Try all drives one by one
for (DWORD dw = 0; dw < dwLeng; dw += 4)
{
// Look only for CD-ROM drives that has a disc with required (.ifo) files
if (DRIVE_CDROM == GetDriveType(pszTemp))
{
TCHAR szDVDPath1[MAX_PATH]
TCHAR szDVDPath2[MAX_PATH];
lstrcpyn(szDVDPath1, pszTemp, 4);
lstrcpyn(szDVDPath 2, pszTemp, 4);
lstrcat(szDVDPath1, TEXT("Video_ts\\Video_ts.ifo"));
lstrcat(szDVDPath2, TEXT("Video_ts\\Vts_01_0.ifo"));
// If the .ifo files exist on this drive then it has a valid DVD-V disc
if (DoesFileExist(achDVDPath1) && DoesFileExist(achDVDPath2))
{
lstrcpyn(pszDrive, pszTemp, 3);
return TRUE; // return the first drive that has a valid DVD-V disc
}
}
pszTemp += 4;
}
return FALSE ; // didn't find any DVD drive (with DVD-V content)
} //End function GetDriveLetter
/////////////////////////////////////////////////////////////////////
// DoesFileExist : Function to determine if the given filename is an existing file.
//
// Parameters:
// In: pszFile - filename string to check for existence.
// Out: none.
//
// Returns:
// TRUE: if the specified file is found.
// FALSE: if the specified file is not found.
/////////////////////////////////////////////////////////////////////
BOOL DoesFileExist(LPTSTR pszFile)
{
HANDLE hFile = NULL ;
// We don't want any error message box to pop up when we try to test
// if the required file is available to open for read.
UINT uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS |
SEM_NOOPENFILEERRORBOX);
hFile = CreateFile(pszFile, GENERIC_READ,
FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
SetErrorMode(uErrorMode); // restore error mode
If (INVALID_HANDLE_VALUE == hFile)
return FALSE ;
CloseHandle(hFile);
return TRUE;
} //End function DoesFileExist